Функция, вызываемая во время уничтожения объекта

#c #destructor

#c #деструктор

Вопрос:

Не могли бы вы предоставить пример кода, отражающий следующее правило:

N3797 c 14, раздел 3.6.3/2:

Если функция содержит объект блочной области статического или потокового хранения, который был уничтожен, и функция вызывается во время уничтожения объекта со статическим или потоковым хранением, программа имеет неопределенное поведение, если поток управления проходит через определение ранее уничтоженного объекта блочной области.

Комментарии:

1. Существует множество способов написания кода, к которому применим этот параграф. Кажется, что вы хотите это для иллюстрации, потому что в этом абзаце есть что-то, чего вы не понимаете. Можете ли вы рассказать нам подробнее о том, чего именно вы не понимаете, чтобы это можно было проиллюстрировать?

2. @PlasmaHH Не могли бы вы объяснить, что поток управления проходит через определение некоторого объекта . Для меня это немного непонятно.

3. @DmitryFucintv Неофициально говоря, если бы вы пошагово просматривали код в отладчике, это означает, что определение стало бы следующим шагом.

4. @DmitryFucintv: строки выполняются в таком порядке: 40, 41, 42, в то время как строка 41 содержит определение функции local static.

Ответ №1:

Вот и все:

 void theFunction()
{
  static std::unique_ptr<int> foo { new int(42) };
}

struct Creator
{
  Creator() { theFunction(); }
};


struct Destroyer
{
  ~Destroyer() { theFunction(); }
};

Destroyer d;
Creator c;

int main()
{}
  

d создается первой, но ее конструктор ничего не делает. Затем c создается и, как часть ее инициализации, theFunction() вызывается, что приводит к инициализации переменной static-storage-duration области видимости блока foo .

Затем, при выходе из программы, объекты статического хранилища уничтожаются в порядке, обратном построению. Так foo уничтожается, а затем c . Наконец, d уничтожается, но вызывается ее деструктор theFunction() , который заставляет поток управления снова достигать определения foo после того, как он уже был уничтожен.

Стандартная цитата, которую вы показали, приписывает этому неопределенное поведение.

Комментарии:

1. Хороший ответ. Было бы еще лучше, если бы у foo был нетривиальный деструктор?

2. @david.pfx Отредактирован в 🙂

3. Спасибо! Как и в OP, я прочитал пункт, на самом деле не «видя» ужасный сценарий, о котором в нем говорится. Пример кода делает это очевидным.

4. Поскольку foo уничтожается до c, не проще ли было бы продемонстрировать U.B. удалив одну структуру и вызвав theFunction() как из конструктора, так и из деструктора?

5. Я думаю, что foo должен быть объектом для применения правила. Теперь все в порядке.

Ответ №2:

 struct Counter {
  int *ptr;
  Counter() {ptr = new int; *ptr = 0;};
  int next() {return (*ptr)  ;}
  ~Counter() {delete ptr;}
};

int function() {
  static Counter c;
  return c.next();
}

struct Obj {
  ~Obj() {cout<<function()<<endl;}  
};

Obj obj;

int main() {
  cout<<function()<<endl;
}
  

Сначала создается obj. Затем создается функция main calls и счетчик c. Когда программа завершается, возможно, что счетчик c уничтожается до obj. Когда объект уничтожен, функция должна быть выполнена, но ее статическая переменная исчезла… итак, неопределенное поведение.