Здравствуйте, Аноним, Вы писали:
А>Здравствуйте, 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>Если считать нормальной ситуацией неопределенное поведение программы — то действительно, все это дело десятое. В добрый путь.
Моё отношение к ситуации, в общем-то, было в последнем абзаце.