delete в Деструкторе
От: buka123  
Дата: 19.08.09 05:14
Оценка: 4 (2)
~machine()
{
for (list<state*>::iterator it=states.begin();it!=states.end();it++)
delete (*it);
}
Достаточно ли этого. Или надо окружить try... catch?
Re: delete в Деструкторе
От: catBasilio  
Дата: 19.08.09 06:12
Оценка:
Здравствуйте, buka123, Вы писали:

B>~machine()

B>{
B>for (list<state*>::iterator it=states.begin();it!=states.end();it++)
B> delete (*it);
B>}
B>Достаточно ли этого. Или надо окружить try... catch?

я бы вообще shared_ptr использовал


class machines
{
 
...
private:
  std::list<std::tr1::shared_ptr<state> > states;
}


а то, в каком-нибудь месте забудешь объекты поудалять и мемори лик будет.
UNIX way — это когда тебе вместо туалетной бумаги дают топор, рубанок и карту близлежащего леса
Re: delete в Деструкторе
От: rg45 СССР  
Дата: 19.08.09 06:18
Оценка:
Здравствуйте, 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]

Александреску и Саттер в своей книге Стандарты программирования на C++
Автор(ы): Герб Саттер, Андрей Александреску

Эта книга поможет новичку стать профессионалом, так как в ней
представлен сконцентрированный лучший опыт программистов на C++,
обобщенный двумя экспертами мирового класса. Начинающий программист
найдет в ней простые и понятные рекомендации для ежедневного
использования, подкрепленные примерами их конкретного применения
на практике.
также говорят о том, что все исключения от внутренних вызовов в деструкторах должны перехватываться. Но вот вопрос: а что делать с внутренними вызовами других деструкторов? Ведь если мы работаем с "правильными" типами, то имеем право расчитавать на то, что эти деструкторы также не бросают исключений. С другой строны, при вызове оператора delete не исключена возможность возникновения исключения за пределами деструктора — если запорчена память, например. Но вот, например, boost::shared_ptr в своем деструкторе не перехватывает исключений, возникающих при освобождении адресуемого объекта. И на мой взгляд было бы странно, если бы он тупо все перехватывал. Так что я присоединяюсь к вопросу, мне тоже интересно послушать мнения на этот счет.

З.Ы. В данном примере лично я бы помещал в список не голые указатели, а умные, например, все тот же boost::shared_ptr. Но в данном случае это так, мелкая придирка, не меняющая сути проблемы.
--
Не можешь достичь желаемого — пожелай достигнутого.
Re: delete в Деструкторе
От: Quadri  
Дата: 19.08.09 06:44
Оценка:
Здравствуйте, buka123, Вы писали:

B>~machine()

B>{
B>for (list<state*>::iterator it=states.begin();it!=states.end();it++)
B> delete (*it);
B>}
B>Достаточно ли этого. Или надо окружить try... catch?

У меня немного другой вопрос: а в каких случаях вообще может возникать исключение в деструктора нормального объекта? На ум приходит лишь запорченная память — но ведь это явная ошибка программирования, которая обязательно устраняется и проблема по идее исчезает?
Re[2]: delete в Деструкторе
От: Chorkov Россия  
Дата: 19.08.09 08:00
Оценка:
Здравствуйте, Quadri, Вы писали:

Q>Здравствуйте, buka123, Вы писали:



B>>~machine()
B>>{
B>>for (list<state*>::iterator it=states.begin();it!=states.end();it++)
B>>    delete (*it);
B>>}


B>>Достаточно ли этого. Или надо окружить try... catch?


Q>У меня немного другой вопрос: а в каких случаях вообще может возникать исключение в деструктора нормального объекта? На ум приходит лишь запорченная память — но ведь это явная ошибка программирования, которая обязательно устраняется и проблема по идее исчезает?



К сожалению, в стандарт дает всего лишь рекомендацию, а книги Александреску не имеют юридической силы.
Поэтому рассчитывать на отсутствие исключения в деструкторе — нельзя. (Если только класс разрушаемого объекта заранее не известен.) Исключения в деструкторах иногда встречаются в разного рода обертках.

Например, "Очень Злой Сам-себе Буратино " может закрывать транзакцию в деструкторе объекта и бросать исключение, если она не прошла.
При выходе из критической секции, защищающей работы с набором глобальных переменных, "Некто" может вписать ассерт, проверяющей согласованность значений глобальных переменных, который со временем прорефакторится до "проверка инварианта + throw" (тоже транзакция, но хорошо замаскированная).
Очевидно, это ошибки проектирования, но никто не даст гарантии что что работать придется только с правильно спроектированным кодом.
Re[2]: delete в Деструкторе
От: Alexander G Украина  
Дата: 19.08.09 08:02
Оценка: 6 (1) +5
Здравствуйте, rg45, Вы писали:

R>Но вот вопрос: а что делать с внутренними вызовами других деструкторов? Ведь если мы работаем с "правильными" типами, то имеем право расчитавать на то, что эти деструкторы также не бросают исключений. С другой строны, при вызове оператора delete не исключена возможность возникновения исключения за пределами деструктора — если запорчена память, например. Но вот, например, boost::shared_ptr в своем деструкторе не перехватывает исключений, возникающих при освобождении адресуемого объекта. И на мой взгляд было бы странно, если бы он тупо все перехватывал. Так что я присоединяюсь к вопросу, мне тоже интересно послушать мнения на этот счет.


Порча памяти произошла уже в результате UB. Да и не ловить такие случаи нужно, а напротив, дать SEH-исключению дойти до обработки ОС, чтобы подключить дебаггер или создать креш-дамп и остановить работу.

В другой книге Саттера
Автор(ы): Герб Саттер

В данном издании объединены две широко известные профессионалам в области
программирования на C++ книги Герба Саттера Exceptional C++
и More Exceptional C++, входящие в серию книг C++ In-Depth,
редактором которой является Бьерн Страуструп, создатель языка C++.
Материал этой книги составляют переработанные задачи серии Guru of the Week,
рассчитанные на читателя с достаточно глубоким знанием C++.
Однако книга будет полезна каждому, кто хочет углубить свои знания в этой
области.
 было показано, что если деструкторы могут кидать исключения, то даже со встроенными массивами будут проблемы. Задача 2.9, с 129-131.

Не кидать и не ловить.
Русский военный корабль идёт ко дну!
Re: delete в Деструкторе
От: Alexander G Украина  
Дата: 19.08.09 08:15
Оценка:
Здравствуйте, buka123, Вы писали:

B>Достаточно ли этого. Или надо окружить try... catch?


Если state кидает, то прийдётся ловить. И тогда прийдётся не следовать совету использовать смарт-поинтеры или библиотеку, реализующую контейнеры для указателей.

Лучше обеспечить, чтобы state не бросал, тогда можно будет применять смартпоинтеры, ну и вообще, см. выше в теме
Автор: Alexander G
Дата: 19.08.09
ссылку на Саттера. Там как раз этот случай рассмотрен.
Русский военный корабль идёт ко дну!
Re[2]: delete в Деструкторе
От: Юрий Жмеренецкий ICQ 380412032
Дата: 19.08.09 08:19
Оценка:
Здравствуйте, Quadri, Вы писали:

Q>У меня немного другой вопрос: а в каких случаях вообще может возникать исключение в деструктора нормального объекта?


Вызов функции вроде flush в дестукторе с последующим возбуждением исключения в случае ошибки. Лучшим решением будет предоствление специальной функции для тих целей: там где действительно необходимо узнать об ошибке — будет явный вызов + catch. В деструкторе же исключение нужно подавлять, иначе возникает как минимум озвученная проблема. Или вот такая:

Object t; // деструктор генерирует исключение
//...
throw exception();
//...
Re: delete в Деструкторе
От: LaptevVV Россия  
Дата: 19.08.09 08:24
Оценка:
Здравствуйте, 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 — смотреть надо
Хочешь быть счастливым — будь им!
Без булдырабыз!!!
Re[3]: delete в Деструкторе
От: rg45 СССР  
Дата: 19.08.09 11:05
Оценка:
Здравствуйте, Юрий Жмеренецкий, Вы писали:

ЮЖ>...В деструкторе же исключение нужно подавлять, иначе возникает как минимум озвученная проблема. Или вот такая:


ЮЖ>
ЮЖ>Object t; // деструктор генерирует исключение
ЮЖ>//...
ЮЖ>throw exception();
ЮЖ>//...
ЮЖ>


Давайте пример слегка доработаем.
#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 перехватить в деструкторе этого смарт-поинтера исключениние, возникающее при удалении адресуемого объекта или нет?
--
Не можешь достичь желаемого — пожелай достигнутого.
Re[2]: delete в Деструкторе
От: jazzer Россия Skype: enerjazzer
Дата: 19.08.09 11:24
Оценка: 6 (1) +4
Здравствуйте, rg45, Вы писали:

R>Так что я присоединяюсь к вопросу, мне тоже интересно послушать мнения на этот счет.


Мое мнение — надо по умолчанию предполагать, что деструкторы не бросают исключений, и не писать никаких блокирующих оберток.
Бросающий деструктор — это исключительный случай.
И явно говорить, если мы это делаем.
По умолчанию — деструкторы не бросают, это должно быть политикой.
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[4]: delete в Деструкторе
От: Alexander G Украина  
Дата: 19.08.09 11:36
Оценка:
Здравствуйте, rg45, Вы писали:

R>И снова вопрос, который не дает покоя: были ли должны разработчики boost::shared_ptr перехватить в деструкторе этого смарт-поинтера исключениние, возникающее при удалении адресуемого объекта или нет?


Нет. Они ж не знают, как его обработать.

А синтетический пример можно выдумать такой, что наоборот, разумно пустить исключение дальше:
{
  scoped_ptr<DelayedFileWrite> file(new DelayedFileWrite(name)); // файл с отложенной записью
  ...
  // запись в деструкторе, при ошибке кидает, так надо знать, что ошибка произошла.
}


Как не следует вставлять в контейнер stl объект с необычными конструтором копирования и оператором присвоения, точно так же не следует пихать объект с кидающим деструктором вообще куда либо.
Русский военный корабль идёт ко дну!
Re[5]: delete в Деструкторе
От: Chorkov Россия  
Дата: 19.08.09 12:02
Оценка:
Здравствуйте, 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); 
  //...

  // А если я выну флешку, на которую оба эти файла должны были бы быть записаны ?

  // ...
}
Re: delete в Деструкторе
От: Кодт Россия  
Дата: 19.08.09 12:19
Оценка:
Здравствуйте, buka123, Вы писали:

Немножко красоты к коду:
std::for_each(states.begin(), states.end(), boost::checked_delete<state>);
... << RSDN@Home 1.2.0 alpha 4 rev. 1237>>
Перекуём баги на фичи!
Re[3]: delete в Деструкторе
От: Николай Ивченков  
Дата: 19.08.09 12:55
Оценка: 6 (1)
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.

Re[4]: delete в Деструкторе
От: Юрий Жмеренецкий ICQ 380412032
Дата: 19.08.09 13:35
Оценка: 9 (1)
Здравствуйте, 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) будет:
struct X
{
  api::resource m_resource;

  void close() {
    api::close(m_resource);
  }

  ~X()
  {
    try {
      this->close();
    } catch(const std::exception&)
    {}
  }
};


В двух этих вариантах поведение программы в некоторых местах зависит от успешности api::close, в некоторых нет. Если не замалчивать исключение в деструкторе, то и первый вариант для сохранения идентичности следует переписать так (сюрприз):

struct X
{
  void close()
  {
    int r = api::close(m_resource);
    if(r != OK)
      throw some_exception(r);
  }

  ~X()
  {
     this->close();    
  }
}


Т.е. одновременные призывы не кидать из деструктора и не замалчивать — звучат несколько странно =) Не кидать, и все. Если действиетельно приходится использовать кидающие операции в деструкторе — то оборачивать, т.к. игнорирование в этом случае является (должно по крайней мере) преднамеренным. Здесь можно кинуть камень в огород библиотекам/фреймворками злоупотребляющими исключениями — не в их пределах ответственности решать, является ли диагностированное состояние исключительной ситуаций для вызывающего.
Re[5]: delete в Деструкторе
От: Alexander G Украина  
Дата: 19.08.09 13:40
Оценка:
Здравствуйте, Юрий Жмеренецкий, Вы писали:

ЮЖ>Здесь можно кинуть камень в огород библиотекам/фреймворками злоупотребляющими исключениями — не в их пределах ответственности решать, является ли диагностированное состояние исключительной ситуаций для вызывающего.


И boost::interruption_point() ?
Есть возможность запретить их временно, ещё бы о ней вспомнить когда надо.
Русский военный корабль идёт ко дну!
Re[6]: delete в Деструкторе
От: Юрий Жмеренецкий ICQ 380412032
Дата: 19.08.09 14:19
Оценка:
Здравствуйте, Alexander G, Вы писали:

AG>Здравствуйте, Юрий Жмеренецкий, Вы писали:


ЮЖ>>Здесь можно кинуть камень в огород библиотекам/фреймворками злоупотребляющими исключениями — не в их пределах ответственности решать, является ли диагностированное состояние исключительной ситуаций для вызывающего.


AG>И boost::interruption_point() ?

Это не злоупотребление, ты бы еще throw_exception вспомнил =)
Re[7]: delete в Деструкторе
От: Alexander G Украина  
Дата: 19.08.09 14:58
Оценка:
Здравствуйте, Юрий Жмеренецкий, Вы писали:

ЮЖ>Это не злоупотребление, ты бы еще throw_exception вспомнил =)


Ну как, вот мы в деструкторе ждём, допустим, другой тред, и наш прерывают:
X::~X() {
  m_thd.wait_for();
}


не очевидно, особенно с учётом того, что это исключение редко надо обрабатывать, и оно не std::exception
Русский военный корабль идёт ко дну!
Re: delete в Деструкторе
От: Vamp Россия  
Дата: 19.08.09 15:12
Оценка:
Ни в коем случае не оборачивай. Что ты собираешься делать, если деструктор бросил исключение? В каком состоянии находится объект? Тут уже ничего не исправить все равно, так что пусть программа надежно свалиться — по крайней мере, в трейсе будет четко видно откуда оно пришло и его можно будет пофиксить.
Да здравствует мыло душистое и веревка пушистая.
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.