В данном случае нет никакого смысла обнулять, т.к. после деструктора уже ничего нет и последующее, ошибочное использование указателя уже будет невозможно
PK>>Насколько это pb = 0 может быть нужно и по какой причине? PK>>(Извините, если уже было, не нашел).
J>Это так народ пытается нейтрализовать двойной вызов деструктора
Ну до кучи ещё может быть попытка нейтрализовать использование уже невалидного указателя на A.
A * a = new A();
a->SomeMethod(); // method uses A::pbdelete a;
a->SomeMethod();
Re[2]: Обнуление указателей в деструкторе
От:
Аноним
Дата:
04.06.09 10:25
Оценка:
Здравствуйте, Bell, Вы писали:
B>Здравствуйте, Peter K., Вы писали:
PK>>Насколько это pb = 0 может быть нужно и по какой причине? B>Абсолютно излишнее действие.
Не излишнее, см пост выше. После того как память освободилась, она не обнуляется, и если у тебя остался указатель на кусок памяти, где раньше жил объект, то ты можешь достучатся до полей мертвого объекта (с непредсказуемыми последствиями в дальнейшем). А если обнулишь, то сразу будет АВ шка.
Здравствуйте, Peter K., Вы писали:
PK>Насколько это pb = 0 может быть нужно и по какой причине?
1) Перестраховка. Обращение к деструированному объекту — это уже UB, только характер может меняться.
Диагностировать обращение к неожиданно нулевому указателю легче, чем к невалидному указателю куда-то в середину кучи.
В крайнем случае, быстро поймаешь ошибку защиты памяти. В противном случае, проедешься с бензопилой по чужим данным.
2) Либо неудачный/бездумный рефакторинг.
Написали reset, а потом обнаружили, что он используется лишь в деструкторе. И скопировали туда.
Либо наоборот — собрались писать reset и забросили, так и не выделив из деструктора.
3) Либо это лишь часть кода. А на деле там всё громоздче:
В роли foo() могут быть обратные вызовы каких-то союзнических функций, работающих с этим объектом.
Например, у тебя облако взаимосвязанных объектов, и запускается лавина рекурсивного удаления их всех.
(На мой взгляд, лавины — это дыра в архитектуре, но на скорую руку, бывает, терпимо).
PK>Насколько это pb = 0 может быть нужно и по какой причине? PK>(Извините, если уже было, не нашел).
потому что кто нить потом может сделать например так:
class B;
class A {
B * pb;
public:
A() : pb(new B()) {}
~A()
{
delete pb;
pb = 0;
CleanupAll();
}
void CleanupAll()
{
....
pb->Cleanup();
...
}
};
Tсли будет обнуление pb срузу после delete — то написанный кем то потом код сразу свалится в B::Cleanup() при обращении к данным/vftbl pb; А без обнуления будет UB — то бишь может свалится, может не свалится, может свалится потом, может свалится потом у каждого десятого юзера etc
Здравствуйте, R.O. Prokopiev, Вы писали:
B>>Абсолютно излишнее действие.
ROP>Нифига подобного.
ROP>Действие очень даже нужное.
Просвяти, в каких это случаях оно такое нужное?
ROP>Деструктор можно вызывать явно:
С этим никто не спорил
А вот обращение к полям разрушенного объекта — UB.
3.8/5
...
If the object will be or was of a non-POD class type, the program has undefined behavior if:
— the pointer is used to access a non-static data member or call a non-static member function of the object
Здравствуйте, Bell, Вы писали:
ROP>>Действие очень даже нужное. B>Просвяти, в каких это случаях оно такое нужное?
Видел явный вызов деструктора в потрохах CArray
(еще в те времена когда не перешел на STL).
Глянул в VC8 — осталось.
Посмотрел в стандарт.
Признаться был не совсем прав.
Многократный вызов деструктора чреват UB.
12.4/14
Once a destructor is invoked for an object, the object no longer exists; the behavior is undefined if the
destructor is invoked for an object whose lifetime has ended (3.8). [ Example: if the destructor for an automatic
object is explicitly invoked, and the block is subsequently left in a manner that would ordinarily
invoke implicit destruction of the object, the behavior is undefined. ]
В принципе все уже сказано. Но все равно добавлю
Я, например, всегда так делаю, ибо AV отладить куда проще, особенно если мозги позволили добавить в рогу создание crash-dump'ов.
При наличие того другого отладка кривых ручек становится необычайно легкой (в сравннии с тем самым UB).
Ибо UB — это попытка доказать, что ты и не хотел быть программистом, а, так, случайно зашел
Здравствуйте, Кодт, Вы писали:
К>Здравствуйте, Peter K., Вы писали: PK>>Насколько это pb = 0 может быть нужно и по какой причине?
К>3) Либо это лишь часть кода. А на деле там всё громоздче:
Вот так хочешь людям поведать, где и почему оно может пригодится, а Кодт уже тут, как тут.
Думаю, что обнулять указатели стоит, даже в деструкторах. Вопрос лишь в том, не будет ли компилятор производить какие-либо оптимизации тут?
Re: Обнуление указателей в деструкторе
От:
Аноним
Дата:
04.06.09 19:32
Оценка:
Здравствуйте, Peter K., Вы писали:
PK>Насколько это pb = 0 может быть нужно и по какой причине?
Про то, что в случае проблем эти самые проблемы будет искать легче уже сказали.
Но лучше это дело автоматизировать и пользоваться умными указателями.
Здравствуйте, R.O. Prokopiev, Вы писали:
ROP>Здравствуйте, Bell, Вы писали:
PK>>>Насколько это pb = 0 может быть нужно и по какой причине? B>>Абсолютно излишнее действие.
ROP>Нифига подобного. ROP>Действие очень даже нужное. ROP>Деструктор можно вызывать явно:
ROP>
ROP> A a;
ROP> a.~A();
ROP> a.~A();
ROP>
Можно, конечно. А можно и явно два раза "delete pb;" сделать, тоже никто не запрещает.
Здравствуйте, Аноним, Вы писали:
А>Здравствуйте, Peter K., Вы писали:
PK>>Насколько это pb = 0 может быть нужно и по какой причине?
А>Про то, что в случае проблем эти самые проблемы будет искать легче уже сказали. А>Но лучше это дело автоматизировать и пользоваться умными указателями.
А за "умные" указатели в иных местах и больно бьют.
Здравствуйте, Ulitka, Вы писали:
U>А за "умные" указатели в иных местах и больно бьют.
Типа "обжегшись на молоке, дуют на воду"?
Ну есть такие, которые тупо отсекают многие полезные вещи.
Фанатизм и фундаментализм можно обсуждать долго, но уже не интересно.
Здравствуйте, Peter K., Вы писали:
PK>Иногда приходится нечто подобное:
PK>Насколько это pb = 0 может быть нужно и по какой причине? PK>(Извините, если уже было, не нашел).
В далеком детстве, я как-то раз раступил на грабли BCB, которые дважды вызывал деструктор статического объекта. До сих пор вспоминаю, и, из вариантов — обнулять / не обнулять, выбираю — обнулять
Вообще, в отладочном коде, иногда полезно в деструкторе возводить какой-нибуть отладочный флаг объекта "типа я разрушен". А в методах его проверять. Незаменимая штука в нетривиальных объектах, у которых подобъекты могут дергать методы родителя
----
К проблемам многопоточного кода это имеет очень косвенное отношение.
-- Пользователи не приняли программу. Всех пришлось уничтожить. --
Здравствуйте, hramovnik, Вы писали:
ROP>>>Действие очень даже нужное. B>>Просвяти, в каких это случаях оно такое нужное?
H> Например, если ты вернешь объект с указателем членом класса, то будет вызвано 2 дестр. Второй соответственно умрет, если нет обнуления.
Ничего не понял. Можно на примере?
Здравствуйте, Bell, Вы писали:
B>Здравствуйте, hramovnik, Вы писали:
ROP>>>>Действие очень даже нужное. B>>>Просвяти, в каких это случаях оно такое нужное?
H>> Например, если ты вернешь объект с указателем членом класса, то будет вызвано 2 дестр. Второй соответственно умрет, если нет обнуления. B>Ничего не понял. Можно на примере?
Можно.
class A
{
public:
A(type *p = NULL)
: ptr(p)
{};
~A()
{
if(ptr)
{
delete ptr;
ptr = NULL;
}
};
type *ptr;
};
A some_function()
{
A a(new type(/*init*/));
/*some code*/return a;
}
void main()
{
A a = some_function();
}
Здравствуйте, Plague, Вы писали:
P>Вот так хочешь людям поведать, где и почему оно может пригодится, а Кодт уже тут, как тут. P>Думаю, что обнулять указатели стоит, даже в деструкторах. Вопрос лишь в том, не будет ли компилятор производить какие-либо оптимизации тут?
кстати компилер вполне может выкинуть такой код. Вспомнился Шнайер (?) с примеров когда в функции шифрование перед выходом обнулялся ключ через memset, типа чтобы все секурно и на стеке не оставить данные, а умный компилер, видя что к обнуляемым локальным данным дальше обрашений нет выкидывал этот memset. В некоторых криптолибах это дело специальным образом обходится.
Здравствуйте, hramovnik, Вы писали:
H>Здравствуйте, Bell, Вы писали:
B>>Здравствуйте, hramovnik, Вы писали:
ROP>>>>>Действие очень даже нужное. B>>>>Просвяти, в каких это случаях оно такое нужное?
H>>> Например, если ты вернешь объект с указателем членом класса, то будет вызвано 2 дестр. Второй соответственно умрет, если нет обнуления. B>>Ничего не понял. Можно на примере?
второй умрёт даже если было обнуление.
Значение указателя скопируется в другой объект ещё до обнуления, и в деструкторе второго будет пытаться удалить уже удалённый объект.
Два деструктора сами по себе(не явно) никогда не вызовутся.
PK>Насколько это pb = 0 может быть нужно и по какой причине? PK>(Извините, если уже было, не нашел).
иногда надо, и с таким сталкивался, когда в деструкторе стоит эвент говорящий что объект уничтожается, а обработчик может обратится к этой переменной, а то и косвенно вызвать деструктор еще раз
Я изъездил эту страну вдоль и поперек, общался с умнейшими людьми и я могу вам ручаться в том, что обработка данных является лишь причудой, мода на которую продержится не более года. (с) Эксперт, авторитет и профессионал из 1957 г.
Здравствуйте, Ulitka, Вы писали:
U>Здравствуйте, R1K0, Вы писали:
RK>>AV отладить куда проще
U>А вот и нет. Точно так же просто, как и обращение к уже "удаленному" сегменту памяти.
Для релиза не факл, а AV 100%
Здравствуйте, Kingofastellarwar, Вы писали:
K>иногда надо, и с таким сталкивался, когда в деструкторе стоит эвент говорящий что объект уничтожается, а обработчик может обратится к этой переменной, а то и косвенно вызвать деструктор еще раз
Здравствуйте, ffk, Вы писали:
ffk>Здравствуйте, Kingofastellarwar, Вы писали:
K>>иногда надо, и с таким сталкивался, когда в деструкторе стоит эвент говорящий что объект уничтожается, а обработчик может обратится к этой переменной, а то и косвенно вызвать деструктор еще раз
ffk>Ужас! А как же нормальная синхронизация?
Не понял о какой именно синхронизации ты говоришь, но как чел, у которого были такие забубенные хороводы объектов (и лично наступавшего на подобные грабли), могу сказать что деструктор любого объекта, который держит в себе другие объекты и обрабатывает их уведомления, перво-наперво должен отключаться от уведомлений дочерних объектов. В особо "тяжелых" случаях, нужно еще форсированно отключить всех собственных подписчиков.
До кучи (для упрощения выискивания причины падений) нужно завести флаг о котором я тут раньше упомянул ...
ROP>>Действие очень даже нужное. B>Просвяти, в каких это случаях оно такое нужное?
Г-н Саттер даёт следующий пример:
void f()
{
T t(1);
T & rt = t;
// <1> действия с t или rt
t.~T();
new (&t) T(2);
// <2> действия с t или rt
// автоуничтожение t
}
И пишет, что данный код абсолютно легален и корректен до тех пор, пока есть
гарантия того, что выполнение "доберётся" до блока <2>:
если конструктор T(2) кинет исключение, будет автоматически вызван деструктор
для уже уничтоженного объекта
Здравствуйте, _wf, Вы писали:
B>>Просвяти, в каких это случаях оно такое нужное?
_wf>Г-н Саттер даёт следующий пример:
_wf>
_wf>void f()
_wf>{
_wf> T t(1);
_wf> T & rt = t;
_wf> // <1> действия с t или rt
_wf> t.~T();
_wf> new (&t) T(2);
_wf> // <2> действия с t или rt
_wf> // автоуничтожение t
_wf>}
_wf>
_wf>И пишет, что данный код абсолютно легален и корректен до тех пор, пока есть _wf>гарантия того, что выполнение "доберётся" до блока <2>: _wf>если конструктор T(2) кинет исключение, будет автоматически вызван деструктор _wf>для уже уничтоженного объекта
Опять не понял
12.4/14
... the behavior is undefined if the destructor is invoked for an object whose lifetime has ended (3.8).
...
Я конечно понимаю, что в ряде случаев это самое неопределенное поведение является вполне определенным — но закладываться на это без крайней на то необходимости — не есть хорошо, ИМХО.
В данном случае, очевидно, ты имеешь ввиду то обстоятельство, что на некоторых платформах при обнулении указателя в первом вызове деструктора, при повторном вызове деструктора ничего страшного не произойдет. Как я уже сказал — повторный вызов деструктора — это уже UB, и лично меня совершенно не успокаивает тот факт, что в данный момент по счастливому стечению обстоятельств ничего не сломалось и не упало.
_wf>>void f()
_wf>>{
_wf>> T t(1);
_wf>> T & rt = t;
_wf>> // <1> действия с t или rt
_wf>> t.~T();
_wf>> new (&t) T(2);
_wf>> // <2> действия с t или rt
_wf>> // автоуничтожение t
_wf>>}
_wf>>
B>Опять не понял B>
B>12.4/14
B>...
B>the behavior is undefined if the destructor is invoked for an object whose lifetime has ended (3.8).
B>...
Противоречия стандарта вышеприведённому коду не вижу. Время жизни объекта t, объявленного в первой строке функции,
заканчивается (с точки зрения компилятора) перед выходом из функции. Компилятор исправно выполняет свою работу
(а именно — разрушение объекта) как при нормальном, так и при "исключительном" выходе из функции.
Противоречие в том, что в середине функции объект искуственно переконструируется (но это проблема разработчика,
а не компилятора, и выбор такого решения исключительно на совести первого), и, если повторное конструирование
не удаётся (конструктор выбрасывает исключение), происходит, по сути, двойной вызов деструктора.
И в данном конкретном случае, проверка на повторный вызов деструктора будет весьма кстати. Остальное дело
десятое.
Всё это не отменяет того, что для того, чтобы написать подобное, нужны действительно веские причины и полное
понимание нюансов и путей выполнения. Проверка "на всякий случай" имхо — вопрос спорный.
Здравствуйте, _wf, Вы писали:
_wf>Противоречия стандарта вышеприведённому коду не вижу.
Об этом вроде никто и не говорил _wf>Время жизни объекта t, объявленного в первой строке функции, _wf>заканчивается (с точки зрения компилятора) перед выходом из функции. Компилятор исправно выполняет свою работу _wf>(а именно — разрушение объекта) как при нормальном, так и при "исключительном" выходе из функции.
_wf>Противоречие в том, что в середине функции объект искуственно переконструируется
В С++ нет такого понятоя — "переконструирование объекта".
_wf>...(но это проблема разработчика, _wf>а не компилятора, и выбор такого решения исключительно на совести первого), и, если повторное конструирование _wf>не удаётся (конструктор выбрасывает исключение), происходит, по сути, двойной вызов деструктора.
По сути происходит принудительное окончание времени жизни объекта при явном вызове деструктора. Если конструирование нового объекта на месте старого прошло неудачно, то мы имеем вызов деструктора для уже мертвого объекта — UB.
_wf>И в данном конкретном случае, проверка на повторный вызов деструктора будет весьма кстати. Остальное дело _wf>десятое.
Если считать нормальной ситуацией неопределенное поведение программы — то действительно, все это дело десятое. В добрый путь.
Здравствуйте, Bell, Вы писали:
B>Здравствуйте, _wf, Вы писали:
_wf>>Противоречия стандарта вышеприведённому коду не вижу. B>Об этом вроде никто и не говорил _wf>>Время жизни объекта t, объявленного в первой строке функции, _wf>>заканчивается (с точки зрения компилятора) перед выходом из функции. Компилятор исправно выполняет свою работу _wf>>(а именно — разрушение объекта) как при нормальном, так и при "исключительном" выходе из функции.
_wf>>Противоречие в том, что в середине функции объект искуственно переконструируется B>В С++ нет такого понятоя — "переконструирование объекта".
Никто и не говорит про понятия C++ — придираться к словам не обязательно.
Мог написать вашим штилем —
"происходит принудительное окончание времени жизни объекта при явном вызове деструктора"
как в этом ключе завернуть про его reborn даже не знаю.
_wf>>...(но это проблема разработчика, _wf>>а не компилятора, и выбор такого решения исключительно на совести первого), и, если повторное конструирование _wf>>не удаётся (конструктор выбрасывает исключение), происходит, по сути, двойной вызов деструктора. B>По сути происходит принудительное окончание времени жизни объекта при явном вызове деструктора. Если конструирование нового объекта на месте старого прошло неудачно, то мы имеем вызов деструктора для уже мертвого объекта — UB.
Вполне предсказуемое такое U/B, соответственно, может быть проконтроллировано.
_wf>>И в данном конкретном случае, проверка на повторный вызов деструктора будет весьма кстати. Остальное дело _wf>>десятое. B>Если считать нормальной ситуацией неопределенное поведение программы — то действительно, все это дело десятое. В добрый путь.
Моё отношение к ситуации, в общем-то, было в последнем абзаце.