Здравствуйте, buka123, Вы писали:
B>~machine() B>{ B>for (list<state*>::iterator it=states.begin();it!=states.end();it++) B> delete (*it); B>} B>Достаточно ли этого. Или надо окружить try... catch?
Очень интересный вопрос. В стандарте по этому поводу существует такая рекомендация:
15.2(3)
The process of calling destructors for automatic objects constructed on the path from a try block to a throw-expression is called “stack unwinding.” [Note: If a destructor called during stack unwinding exits with an exception, terminate is called (15.5.1). So destructors should generally catch exceptions and not let them propagate out of the destructor. —end note]
также говорят о том, что все исключения от внутренних вызовов в деструкторах должны перехватываться. Но вот вопрос: а что делать с внутренними вызовами других деструкторов? Ведь если мы работаем с "правильными" типами, то имеем право расчитавать на то, что эти деструкторы также не бросают исключений. С другой строны, при вызове оператора delete не исключена возможность возникновения исключения за пределами деструктора — если запорчена память, например. Но вот, например, boost::shared_ptr в своем деструкторе не перехватывает исключений, возникающих при освобождении адресуемого объекта. И на мой взгляд было бы странно, если бы он тупо все перехватывал. Так что я присоединяюсь к вопросу, мне тоже интересно послушать мнения на этот счет.
З.Ы. В данном примере лично я бы помещал в список не голые указатели, а умные, например, все тот же boost::shared_ptr. Но в данном случае это так, мелкая придирка, не меняющая сути проблемы.
--
Не можешь достичь желаемого — пожелай достигнутого.
Здравствуйте, buka123, Вы писали:
B>~machine() B>{ B>for (list<state*>::iterator it=states.begin();it!=states.end();it++) B> delete (*it); B>} B>Достаточно ли этого. Или надо окружить try... catch?
У меня немного другой вопрос: а в каких случаях вообще может возникать исключение в деструктора нормального объекта? На ум приходит лишь запорченная память — но ведь это явная ошибка программирования, которая обязательно устраняется и проблема по идее исчезает?
B>>Достаточно ли этого. Или надо окружить try... catch?
Q>У меня немного другой вопрос: а в каких случаях вообще может возникать исключение в деструктора нормального объекта? На ум приходит лишь запорченная память — но ведь это явная ошибка программирования, которая обязательно устраняется и проблема по идее исчезает?
К сожалению, в стандарт дает всего лишь рекомендацию, а книги Александреску не имеют юридической силы.
Поэтому рассчитывать на отсутствие исключения в деструкторе — нельзя. (Если только класс разрушаемого объекта заранее не известен.) Исключения в деструкторах иногда встречаются в разного рода обертках.
Например, "Очень Злой Сам-себе Буратино " может закрывать транзакцию в деструкторе объекта и бросать исключение, если она не прошла.
При выходе из критической секции, защищающей работы с набором глобальных переменных, "Некто" может вписать ассерт, проверяющей согласованность значений глобальных переменных, который со временем прорефакторится до "проверка инварианта + throw" (тоже транзакция, но хорошо замаскированная).
Очевидно, это ошибки проектирования, но никто не даст гарантии что что работать придется только с правильно спроектированным кодом.
Здравствуйте, rg45, Вы писали:
R>Но вот вопрос: а что делать с внутренними вызовами других деструкторов? Ведь если мы работаем с "правильными" типами, то имеем право расчитавать на то, что эти деструкторы также не бросают исключений. С другой строны, при вызове оператора delete не исключена возможность возникновения исключения за пределами деструктора — если запорчена память, например. Но вот, например, boost::shared_ptr в своем деструкторе не перехватывает исключений, возникающих при освобождении адресуемого объекта. И на мой взгляд было бы странно, если бы он тупо все перехватывал. Так что я присоединяюсь к вопросу, мне тоже интересно послушать мнения на этот счет.
Порча памяти произошла уже в результате UB. Да и не ловить такие случаи нужно, а напротив, дать SEH-исключению дойти до обработки ОС, чтобы подключить дебаггер или создать креш-дамп и остановить работу.
Здравствуйте, buka123, Вы писали:
B>Достаточно ли этого. Или надо окружить try... catch?
Если state кидает, то прийдётся ловить. И тогда прийдётся не следовать совету использовать смарт-поинтеры или библиотеку, реализующую контейнеры для указателей.
Лучше обеспечить, чтобы state не бросал, тогда можно будет применять смартпоинтеры, ну и вообще, см. выше в теме
Здравствуйте, Quadri, Вы писали:
Q>У меня немного другой вопрос: а в каких случаях вообще может возникать исключение в деструктора нормального объекта?
Вызов функции вроде flush в дестукторе с последующим возбуждением исключения в случае ошибки. Лучшим решением будет предоствление специальной функции для тих целей: там где действительно необходимо узнать об ошибке — будет явный вызов + catch. В деструкторе же исключение нужно подавлять, иначе возникает как минимум озвученная проблема. Или вот такая:
Здравствуйте, buka123, Вы писали:
B>~machine() B>{ B>for (list<state*>::iterator it=states.begin();it!=states.end();it++) B> delete (*it); B>} B>Достаточно ли этого. Или надо окружить try... catch?
По стандарту операция delete исключения не возбуждает.
А вот state — смотреть надо
Хочешь быть счастливым — будь им!
Без булдырабыз!!!
Здравствуйте, Юрий Жмеренецкий, Вы писали:
ЮЖ>...В деструкторе же исключение нужно подавлять, иначе возникает как минимум озвученная проблема. Или вот такая:
ЮЖ>
#include <boost/shared_ptr.hpp>
class A {};
void main()
{
A* a = new A();
boost::shared_ptr<A> p(a); //умный указатель, который не перехватывает исключения в свое деструктореdelete a; //наши кривые руки (упрощенная модель)throw std::exception(); //бросок исключения и в процессе раскрутки стека еще одно исключение, летящее из деструктора boost::shared_ptr
}
И снова вопрос, который не дает покоя: были ли должны разработчики boost::shared_ptr перехватить в деструкторе этого смарт-поинтера исключениние, возникающее при удалении адресуемого объекта или нет?
--
Не можешь достичь желаемого — пожелай достигнутого.
Здравствуйте, rg45, Вы писали:
R>Так что я присоединяюсь к вопросу, мне тоже интересно послушать мнения на этот счет.
Мое мнение — надо по умолчанию предполагать, что деструкторы не бросают исключений, и не писать никаких блокирующих оберток.
Бросающий деструктор — это исключительный случай.
И явно говорить, если мы это делаем.
По умолчанию — деструкторы не бросают, это должно быть политикой.
Здравствуйте, rg45, Вы писали:
R>И снова вопрос, который не дает покоя: были ли должны разработчики boost::shared_ptr перехватить в деструкторе этого смарт-поинтера исключениние, возникающее при удалении адресуемого объекта или нет?
Нет. Они ж не знают, как его обработать.
А синтетический пример можно выдумать такой, что наоборот, разумно пустить исключение дальше:
{
scoped_ptr<DelayedFileWrite> file(new DelayedFileWrite(name)); // файл с отложенной записью
...
// запись в деструкторе, при ошибке кидает, так надо знать, что ошибка произошла.
}
Как не следует вставлять в контейнер stl объект с необычными конструтором копирования и оператором присвоения, точно так же не следует пихать объект с кидающим деструктором вообще куда либо.
Здравствуйте, Alexander G, Вы писали:
AG>Здравствуйте, rg45, Вы писали:
R>>И снова вопрос, который не дает покоя: были ли должны разработчики boost::shared_ptr перехватить в деструкторе этого смарт-поинтера исключениние, возникающее при удалении адресуемого объекта или нет?
AG>Нет. Они ж не знают, как его обработать.
AG>А синтетический пример можно выдумать такой, что наоборот, разумно пустить исключение дальше: AG>
AG>{
AG> scoped_ptr<DelayedFileWrite> file(new DelayedFileWrite(name)); // файл с отложенной записью
AG> ...
AG> // запись в деструкторе, при ошибке кидает, так надо знать, что ошибка произошла.
AG>}
AG>
AG>Как не следует вставлять в контейнер stl объект с необычными конструтором копирования и оператором присвоения, точно так же не следует пихать объект с кидающим деструктором вообще куда либо.
Развивая эту мысль, до логического завершения, объект с кидающим деструктором вообще нельзя использовать.
{
DelayedFileWrite file1(name1);
DelayedFileWrite file2(name2);
//...
// А если я выну флешку, на которую оба эти файла должны были бы быть записаны ?
// ...
}
jazzer:
J>Мое мнение — надо по умолчанию предполагать, что деструкторы не бросают исключений, и не писать никаких блокирующих оберток.
Cобственно, стандартная библиотека C++ именно на таком предположении и работает:
17.4.3.6:
1 In certain cases (replacement functions, handler functions, operations on types used to instantiate standard library template components), the C++ Standard Library depends on components supplied by a C++ program. If these components do not meet their requirements, the Standard places no requirements on the implementation.
2 In particular, the effects are undefined in the following cases:
[...]
— if any replacement function or handler function or destructor operation throws an exception, unless specifically allowed in the applicable Required behavior paragraph.
Здравствуйте, rg45, Вы писали:
... R>Давайте пример слегка доработаем. R>
R>#include <boost/shared_ptr.hpp>
R>class A {};
R>void main()
R>{
R> A* a = new A();
R> boost::shared_ptr<A> p(a); //умный указатель, который не перехватывает исключения в свое деструкторе
R> delete a; //наши кривые руки (упрощенная модель)
R> throw std::exception(); //бросок исключения и в процессе раскрутки стека еще одно исключение, летящее из деструктора boost::shared_ptr
R>}
R>
Откуда здесь исключение в деструкторе shared_ptr? UB при повтороном удалении...
R>И снова вопрос, который не дает покоя: были ли должны разработчики boost::shared_ptr перехватить в деструкторе этого смарт-поинтера исключениние, возникающее при удалении адресуемого объекта или нет?
Нет, они не знают что с ним делать и неизвестно возможно ли оно вообще. Хотя для boost::shared_ptr просто написали "The behavior of the smart pointer templates is undefined if the destructor or operator delete for objects of type T throw exceptions".
Я вот какой случай имею ввиду:
Вариант 1) имеется небросающая функция 'int api::close(resource&)' которая возвращает OK в случае успеха и код ошибки в противном случае. Обертка (или что-то посложнее) для нее может выглядеть так (для упрощения разрешим повторное закрытие, не приводящее ни к чему):
struct X
{
api::resource m_resource;
void close()
{
int r = api::close(m_resource);
if(r != OK)
throw some_exception(r);
}
~X()
{
api::close(m_resource); // nothrow
}
};
Аналогичный вариант для случая с кидающей close (такой вот api) будет:
В двух этих вариантах поведение программы в некоторых местах зависит от успешности api::close, в некоторых нет. Если не замалчивать исключение в деструкторе, то и первый вариант для сохранения идентичности следует переписать так (сюрприз):
struct X
{
void close()
{
int r = api::close(m_resource);
if(r != OK)
throw some_exception(r);
}
~X()
{
this->close();
}
}
Т.е. одновременные призывы не кидать из деструктора и не замалчивать — звучат несколько странно =) Не кидать, и все. Если действиетельно приходится использовать кидающие операции в деструкторе — то оборачивать, т.к. игнорирование в этом случае является (должно по крайней мере) преднамеренным. Здесь можно кинуть камень в огород библиотекам/фреймворками злоупотребляющими исключениями — не в их пределах ответственности решать, является ли диагностированное состояние исключительной ситуаций для вызывающего.
Здравствуйте, Юрий Жмеренецкий, Вы писали:
ЮЖ>Здесь можно кинуть камень в огород библиотекам/фреймворками злоупотребляющими исключениями — не в их пределах ответственности решать, является ли диагностированное состояние исключительной ситуаций для вызывающего.
И boost::interruption_point() ?
Есть возможность запретить их временно, ещё бы о ней вспомнить когда надо.
Здравствуйте, Alexander G, Вы писали:
AG>Здравствуйте, Юрий Жмеренецкий, Вы писали:
ЮЖ>>Здесь можно кинуть камень в огород библиотекам/фреймворками злоупотребляющими исключениями — не в их пределах ответственности решать, является ли диагностированное состояние исключительной ситуаций для вызывающего.
AG>И boost::interruption_point() ?
Это не злоупотребление, ты бы еще throw_exception вспомнил =)
Ни в коем случае не оборачивай. Что ты собираешься делать, если деструктор бросил исключение? В каком состоянии находится объект? Тут уже ничего не исправить все равно, так что пусть программа надежно свалиться — по крайней мере, в трейсе будет четко видно откуда оно пришло и его можно будет пофиксить.