#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. Когда объект уничтожен, функция должна быть выполнена, но ее статическая переменная исчезла… итак, неопределенное поведение.