LINUX.ORG.RU

Деструкторы не нужны (?)

 


0

5

Тут в соседней теме один анон сказанул следующее:

Дело не в том. Сишка, она про написание руками + можно навесит абстракций, аки глиб/гобджект. Кресты же — изначально нагромождение абстракций со строками, срущими в кучу, функциональными объектами, методами, вызывающимися неявно (например, деструкторы, на которые жаловался кармак) и проч

Собственно, хотелось бы поговорить о выделенном.

Антон прикрылся ссылкой, по которой про деструкторы я так ничего и не нашёл. Более того, в твиттере Кармака всё выглядит с точностью до наоборот — https://twitter.com/id_aa_carmack/status/172340532419375104

RAII constructor / destructor pairing and templates for type safe collections are pretty high on my list of C++ benefits over C

Кто прав? Кто виноват? И нужны ли в итоге C++ деструкторы?

Ответ на: комментарий от monk

Сможешь доказать, что он exception-safe?

А это нужно делать мне? Может мне еще нужно доказывать и exception safety для std::map или std::vector?

Что по коду, то его явно можно улучшить. Хотя, с учетом возраста Qt, это еще ничего-так, разобраться можно.

eao197 ★★★★★
()
Ответ на: комментарий от eao197

А это нужно делать мне?

Я привёл пример деструктора, в котором достаточно легко допустить ошибку. Причём, если обычную функция пользователь библиотеки может завернуть в try/catch и отлавливать возможные нештатные ситуации, то от деструктора так не отделаешься.

Пример проявления такой ошибки тоже привёл: http://ogre3d.org/forums/viewtopic.php?f=2&t=65257

Хуже только Perl с обработкой ошибок в стиле close F || die ...

monk ★★★★★
()
Ответ на: комментарий от monk

Я привёл пример деструктора, в котором достаточно легко допустить ошибку.

Если писать в таком стиле, то запросто. Только ведь никто не заставляет писать говнокод, не правда ли?

eao197 ★★★★★
()
Ответ на: комментарий от monk

Хуже только Perl с обработкой ошибок в стиле close F || die ...

Ну и предложите адекватную альтернативу для C++. То говно из Visual Basic, которое вы упомнали выше, ничуть не лучше.

eao197 ★★★★★
()
Ответ на: комментарий от eao197

Только ведь никто не заставляет писать говнокод, не правда ли?

Так какой крупный проект ни возьми, всюду «говнокод». Потому что у почти любого сложного объекта будет также достаточно сложная логика завершения работы с ним. И это нормально. Возможно ненормально запихивать это в деструктор, а надо делать метод finish(). Но тогда деструктор вообще не нужен, всё будет в этом методе (так как всё равно его надо обязательно вызывать). Разве что в деструкторе сделать assert(finished), чтобы видеть, что не забыли завершить работу ценой байта памяти на объект.

monk ★★★★★
()
Ответ на: комментарий от monk

Так какой крупный проект ни возьми, всюду «говнокод»

Тому есть простое объяснение: очень много старых проектов начиналось еще во времена, когда исключения в C++ использовались редко или вообще не использовались. Поэтому в деструкторы пихали код, по типу этого фрагмента из Qt. А когда исключения таки подтянули, то рефакторить старый код уже сильно сложно.

Возможно ненормально запихивать это в деструктор, а надо делать метод finish().

Не надо. Сложный объект, вроде QWidget-а, должен быть агрегатом из более простых объектов, каждый из которых в своем деструкторе чистит свою часть. Тогда не придется ваять деструкторы в виде простыней на три экрана.

eao197 ★★★★★
()
Ответ на: комментарий от eao197

Не надо. Сложный объект, вроде QWidget-а, должен быть агрегатом из более простых объектов, каждый из которых в своем деструкторе чистит свою часть.

Дорогой эксперт :-) Агрегаты и всякие там RAII с шаблонами и смарт-поинтерами - это круто, но знаешь ли ты стоимость лицензии Qt в месяц и сколько постоянных клиентов покупают этот самый код, который содержит «деструкторы в виде простыней на три экрана»? :-)

anonymous
()
Ответ на: комментарий от eao197

Ну и предложите адекватную альтернативу для C++.

Адекватная альтернатива включает либо проверку квалификатора noexcept на всех функциях, вызываемых из деструктора (как сейчас происходит с const), либо обработку исключения в деструкторе как нормальной функции: простейший вариант — в блок catch передавать список всех полученных исключений (чтобы блок несколько раз не выполнялся).

То говно из Visual Basic, которое вы упомнали выше, ничуть не лучше.

То говно позволяет не ронять программу не дав пользователю сохранить данные. А то как в 1С: место на диске кончилось, так «Ошибка сохранения изменений в конфигурации. Программа будет закрыта». И два часа работы заново.

monk ★★★★★
()
Ответ на: комментарий от monk

Адекватная альтернатива включает либо проверку квалификатора noexcept на всех функциях

Вы в курсе, что noexcept не гарантирует, что noexcept-функция завершится успешно? Так что какой толк от этой проверки, не понятно.

либо обработку исключения в деструкторе как нормальной функции:

Как быть с деструкторами полей класса, которые отрабатывают после деструктора самого класса?

простейший вариант — в блок catch передавать список всех полученных исключений (чтобы блок несколько раз не выполнялся).

А память под этот список откуда брать?

eao197 ★★★★★
()
Ответ на: комментарий от eao197

Сложный объект, вроде QWidget-а, должен быть агрегатом из более простых объектов, каждый из которых в своем деструкторе чистит свою часть.

Там 90% деструктора ungrabGesture, setDirtyOpaqueRegion, updateAccessibility, ... и удаление виджета из всех мест, где на него могут быть ссылки.

Конечно, можно разбить деструктор на 20 частей, для каждой сделать свой «объект», который ничего не делает, кроме как чистит свою часть. Но общая сложность от этого не уменьшится, а проследить, что все эти деструкторы на всей глубине вызовов функций нигде не вызывают функцию, которая может бросить исключение, будет даже сложнее.

monk ★★★★★
()
Ответ на: комментарий от eao197

Вы в курсе, что noexcept не гарантирует, что noexcept-функция завершится успешно? Так что какой толк от этой проверки, не понятно.

Если в noexcept запрещено выполнение функции (и операторов), которые могут бросить исключение, то гарантирует. В нынешнем варианте noexcept почти равнозначен assert(0) в catch(...).

monk ★★★★★
()
Ответ на: комментарий от eao197

А память под этот список откуда брать?

Оттуда же, откуда сейчас берётся память под адрес объекта std::exception, например. Можно зарезервировать 256 байт под массив адресов, если вопрос именно о том, как оформить структуру.

Как быть с деструкторами полей класса, которые отрабатывают после деструктора самого класса?

С точки зрения логики деструктор базового класса, деструкторы полей класса и delete должны вызываться даже если деструктор не смог выполнить свою работу.

monk ★★★★★
()
Ответ на: комментарий от monk

Если в noexcept запрещено выполнение функции (и операторов), которые могут бросить исключение, то гарантирует.

Это уже будет какой-то другой noexcept.

Можно зарезервировать 256 байт под массив адресов, если вопрос именно о том, как оформить структуру.

И что делать, если этого не хватит?

С точки зрения логики деструктор базового класса, деструкторы полей класса и delete должны вызываться даже если деструктор не смог выполнить свою работу.

Я не про то. Вот из деструктора поля класса выскочило исключение. Где для него catch указывать?

eao197 ★★★★★
()
Ответ на: комментарий от monk

Там 90% деструктора ungrabGesture, setDirtyOpaqueRegion, updateAccessibility, ... и удаление виджета из всех мест, где на него могут быть ссылки.

Это в результате длительной эволюции Qt с античных времен оно так. Не факт, что по другому сделать нельзя. Например, вот такие строки:

delete d->layout;
d->layout = 0;
...
delete d->needsFlush;
d->needsFlush = 0;
уже вызывают недоумение: почему нельзя было использовать auto_ptr из C++98. Вызовы тех же ungrabGestures могли бы делаться в деструкторе контейнера, который их хранит.

Так что если задастся целью, то упростить и причесать там будет что.

Но общая сложность от этого не уменьшится, а проследить, что все эти деструкторы на всей глубине вызовов функций нигде не вызывают функцию, которая может бросить исключение, будет даже сложнее.

Ну не знаю, убедиться в корректности одной функции на 30 строк вряд ли проще, чем в корректности 10 функций по три строки в каждой.

eao197 ★★★★★
()
Ответ на: комментарий от monk

Хуже только Perl с обработкой ошибок в стиле close F || die ...

Во-первых есть autodie, с которым можно просто сделать close и оно само сдохнет, а во-вторых die это по сути тиот же throw и отлично ловится в eval { expr } (и есть сахарок вроде Try::Tiny или более продвинутых модулей исключений)

annulen ★★★★★
()
Ответ на: комментарий от eao197

Это уже будет какой-то другой noexcept.

Да. Более безопасный для использования.

И что делать, если этого не хватит?

В последнем блоке указать, что список неполон. Тогда у программиста хотя бы будет возможность обработать ситуацию «выскочила дофига исключений».

Вот из деструктора поля класса выскочило исключение. Где для него catch указывать

Поля объекта. Там же, где и для самого объекта. Точка вызова деструктора в программе известна, вокруг этой точки хорошо было бы иметь возможность сделать try.

monk ★★★★★
()
Ответ на: комментарий от monk

Да. Более безопасный для использования.

А вы погуглите, почему в C++11 не сделали контроля за noexcept в compile-time. Не нужно думать, что не было других таких же умных, как вы, которые хотели бы видеть такой контроль в C++.

В последнем блоке указать, что список неполон. Тогда у программиста хотя бы будет возможность обработать ситуацию «выскочила дофига исключений».

Покажите пример кода, как бы это могло выглядеть. Чтобы были catch-и для N исключений, которые удалось сохранить, и еще один catch для «выскочило дофига исключений» И расскажите, плз, что может сделать разработчик с информацией «выскочило дофига исключений».

Поля объекта. Там же, где и для самого объекта. Точка вызова деструктора в программе известна, вокруг этой точки хорошо было бы иметь возможность сделать try.

Покажите, плиз, в примерах кода. Вот, допустим, у меня есть что-то вроде:

class invalid_user : public std::runtime_error {
  std::string username_;
  std::string password_;
public :
  invalid_user(std::string w, std::string n, std::string p)
    : std::runtime_error(move(w)), username_(move(n)), password_(move(p)) {}

  const std::string & username() const { return username_; }
  const std::string & password() const { return password_; }
};
Как мне в invalid_user ловить исключения, которые могут быть выпущены из деструкторов std::string-а (поля username_ и password_).

Заодно и такой вопрос, раз уж вы считаете, что точка вызова деструктора в программе известна. Допустим, есть такой код:

try { ... }
catch(const invalid_user & x) {...}
Где мне писать обработчик исключения, выпущенного из деструктора invalid_user после выхода из catch?

Или вот в такой ситуации:

std::unique_ptr<some_object> create_some_object() {
  static some_object_factory_t factory;
  return factory.create();
}
Допустим, деструктор some_object_factory_t может бросать исключения. Где и как написать для них обработчик?

eao197 ★★★★★
()
Ответ на: комментарий от eao197

почему нельзя было использовать auto_ptr из C++98

Тут согласен. Хотя, возможно, в этом случае нельзя было бы гарантировать порядок удаления. Например, если эти поля обязательно очищать до того, как убирать виджет из списка фокусов.

Вызовы тех же ungrabGestures могли бы делаться в деструкторе контейнера, который их хранит.

Насколько я понял код, не могли бы. Этот контейнер живёт намного дольше чем виджет. А жесты, связанные с виджетом при смерти виджета из него должны удаляться.

Кстати, вот как раз типовой пример. При удалении объекта его надо удалить из всех коллекций, кэшей и т.д., где он находится. То, что в SQL называется каскадным удалением. Но если в SQL каскадное удаление может откатиться (например, если какая-то коллекция должна иметь хотя бы один элемент), то при удалении через деструктор дороги назад нет.

monk ★★★★★
()
Ответ на: комментарий от monk

Хотя, возможно, в этом случае нельзя было бы гарантировать порядок удаления.

В этом случае все равно было бы проще:

d->layout.reset();
...
d->needsFlush.reset();

Кстати, вот как раз типовой пример. При удалении объекта его надо удалить из всех коллекций, кэшей и т.д., где он находится.

Блин, ну вот вы серьезно сейчас? В любом другом языке, где используются мутабельные структуры данных, при изъятии объекта из списков, в которых он зарегистрирован, будет точно такая же жопа. И деструкторы C++ здесь вообще не причем.

eao197 ★★★★★
()
Ответ на: комментарий от dzidzitop

На колу мочало, начинаем все сначала... Очень хочется верить, что вы сейчас всерьез говорите, а не троллите.

Итак, показанный класс дает пользователю возможность закрыть файл через явный вызов close и узнать о возможных проблемах.

Но, если пользователь этого по каким-то причинам не сделал (например, из-за раскрутки стека при исключении или из-за того, что программа завершается, а экземпляр file_wrapper-а был статиком), то деструктор хотя бы почистит те ресурсы, которые под этот файл были выделены в приложении.

Как по мне, так это много лучше, чем отказ от попытки вызвать close в деструкторе.

eao197 ★★★★★
()
Ответ на: комментарий от eao197

то деструктор хотя бы почистит те ресурсы, которые под этот файл были выделены в приложении.

в программировании мне важнее, чтобы пользователь гарантированно увидел ошибку, чтобы ошибка была исправлена, а не чтобы класс попробовал хоть как-то (тихо) прибраться после выполнения некорректного кода. каждому - своё.

к слову,

https://github.com/dzidzitop/libafc/blob/master/src/afc/stream.cpp#L137

тут я обрываю программу, ошибок не проглатываю даже в деструкторе.

а нет, проглатываю. но, наверное, надо переписать.

dzidzitop ★★
()
Последнее исправление: dzidzitop (всего исправлений: 1)
Ответ на: комментарий от dzidzitop

к слову,
https://github.com/dzidzitop/libafc/blob/master/src/afc/stream.cpp#L137
тут я обрываю программу, ошибок не проглатываю даже в деструкторе.

К слову, мне кажется, что я разговариваю с шизофренником, который говорит одно, а делает совсем другое:

afc::FileInputStream::~FileInputStream()
{
	closeFileNoexcept(m_file, function<int (FILE *)>(fclose));
}
...
	template<typename FileType, typename CloseFunction>
	inline void closeFileNoexcept(FileType * const file, CloseFunction close) noexcept
	{
		if (file == nullptr) {
			return;
		}
		close(file); // ignoring any potential fclose failure
}

В вашем коде обнаруживается слишком уж много расхождений с вашими же словами. Поэтому ваше мнение спокойно можно направлять в /dev/null.

eao197 ★★★★★
()
Ответ на: комментарий от eao197

как уже написал выше, этот код я, вероятно, перепишу. Таки от говнокода нужно избавляться.

dzidzitop ★★
()
Последнее исправление: dzidzitop (всего исправлений: 1)
Ответ на: комментарий от eao197

уже вызывают недоумение: почему нельзя было использовать auto_ptr из C++98.

Никому он в Qt не упал ни тогда, ни сейчас — в Qt нет исключений, что отрадно, и свой ресурс менеджмент, без этих ваших смарт-поинтеров из коробки!!11

anonymous
()
Ответ на: комментарий от eao197

продолжу дискуссию в адекватном ключе. для случаев, когда тихо попробовать закрыть файл нормально, а чуть что, то и не важно, что файл не закрылся, можно использовать guard идиому. То есть, создать FileGuard, и прикреплять его к конкретному файлу. Таких FileGuard может быть много, для каждой конкретной ситуации. В деструкторах там можно делать что угодно, в том числе имея контекст выполнения.

dzidzitop ★★
()
Ответ на: комментарий от dzidzitop

Еще чуть-чуть и вы поймете, что показанный выше file_wrapper — это и есть тот самый FileGuard. И что первично иметь небросающие деструкторы, которые предпринимают попытку почистить ресурсы. Т.к. над этим уже можно строить такие многоэтажные конструкции, которые вам захочется. Но не наоборот.

eao197 ★★★★★
()
Ответ на: комментарий от dzidzitop

В деструкторах там можно делать что угодно, в том числе имея контекст выполнения.

Просто RAII в C++ можно переименовывать в MAII — Memory Acquisition Is Initialization :-) В остальных случаях деструкторы чисто для подстрахоуки :-)

anonymous
()
Ответ на: комментарий от eao197

погуглите, почему в C++11 не сделали контроля за noexcept в compile-time

Потому что в старом C++ корректно

extern void f() throw(X, Y);

void g() throw(X) {
    f(); // OK
}

А дальше багосовместимость... Хотя никто не мешает кидать Warning на такое безобразие.

Или я не то нагуглил?

Покажите пример кода, как бы это могло выглядеть. Чтобы были catch-и для N исключений, которые удалось сохранить, и еще один catch для «выскочило дофига исключений»

try { .... } 
catch (MyOverflow &e) { /* поймали своё исключение */ }
catch (exception &e) { /* поймали exception */ }
catch (exception *e) { /* поймали много exception */
  exception *last = e + 256; // здесь, конечно, лучше поименовать  константу
  while (*e) { /* Здесь *e -- обрабатываемое исключение */; e++ }
  if (last == e) { /* исключений было дофига  */ }
}
catch (...) { /* поймали что-то ещё */ }
monk ★★★★★
()
Ответ на: комментарий от eao197

ловить исключения, которые могут быть выпущены из деструкторов std::string-а (поля username_ и password_).

Также как и из самого деструктора:

try {
  invalid_user user;
} catch (...) { здесь ловим }

Где мне писать обработчик исключения, выпущенного из деструктора invalid_user после выхода из catch?

try {
  try { ... }
  catch(const invalid_user & x) {...}
} catch (...) { была ошибка в обработчике ошибок. возможно в деструкторе invalid_user }
monk ★★★★★
()
Ответ на: комментарий от eao197

Допустим, деструктор some_object_factory_t может бросать исключения. Где и как написать для них обработчик?

Деструктор статической переменной должен вызваться только при окончании программы? Тогда только что-то вроде main () { try { .... } catch (...) { здесь в том числе и статические деструкторы } }

monk ★★★★★
()

Что мешает оборачивать контекст в класс с RAII, а потом уже после того, как деструктор свое отработает, то мы можем у контекста узнать с каким результатом завершился деструктор.

https://ideone.com/4BYwQ5

Gorthauer ★★★★★
()
Последнее исправление: Gorthauer (всего исправлений: 1)
Ответ на: комментарий от eao197

Блин, ну вот вы серьезно сейчас? В любом другом языке, где используются мутабельные структуры данных, при изъятии объекта из списков, в которых он зарегистрирован, будет точно такая же жопа.

Не-а. Со сборщиком мусора, финализаторами и списками со слабыми ссылками всё замечательно. Финализатор может бросать исключения, а финализирующий поток может решить что делать, вплоть до отмены ликвидации объекта (например, записав его ещё в какой-нибудь список).

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

monk ★★★★★
()
Ответ на: комментарий от monk

Или я не то нагуглил?

Полагаю, что не то: http://stackoverflow.com/questions/2762078/why-is-c0xs-noexcept-checked-dynam...

catch (exception &e) { /* поймали exception */ }
catch (exception *e) { /* поймали много exception */ }

Это будет уже другой C++, т.к. сейчас в C++ можно ловить исключение по указателю на него. И это вовсе не означает поймали много exception.

Также как и из самого деструктора

Явно мы друг друга не понимаем. То, что вы написали уже есть и работает. Попробуйте написать вариант, когда исключения из деструкторов invalid_user::username_ и invalid_user::password_ перехватываются и обрабатываются до вызова деструктора std::runtime_error.

try {
  try { ... }
  catch(const invalid_user & x) {...}
} catch (...) { была ошибка в обработчике ошибок. возможно в деструкторе invalid_user }

Вы не понимаете. Есть сценарий:

a();
try { ... }
catch( const invalid_user & x ) {...}
b();
В этом сценарии все исключения, которые не являются invalid_user, должны уходить наверх. А вот исключение invalid_user должно быть обработано и затем должна быть вызвана функция b().

Однако, если следовать вашим желаниям, можно столкнуться с ситуацией, когда деструктор invalid_user выпустит исключение. До вызова b(). Именно эту ситуацию и требуется обработать, чтобы b() таки была вызвана.

Как вы это себе представляете в коде?

eao197 ★★★★★
()
Ответ на: комментарий от monk

Не-а. Со сборщиком мусора

В C++ нет сборщика мусора.

eao197 ★★★★★
()
Ответ на: комментарий от Gorthauer

Что мешает оборачивать контекст в класс с RAII, а потом уже после того, как деструктор свое отработает, то мы можем у контекста узнать с каким результатом завершился деструктор.

Отличная идея! :-) В лучших традициях жанра «простые вещи в C++ делаются сложно, а сложные не делаются вообще» :-)

anonymous
()
Ответ на: комментарий от anonymous

Ха. Ха-ха-ха. UB.

Что ещё ты знаешь из примеров Википедии и о неопределённом поведении в Ц? :-) Монк знает десяток языков программирования и смотрит с творческой позиции, с позиции изобретателя, а не какого-нибудь покорного пользователя какого-нибудь std::map, толком не знающего на деле что такое красно-чёрные деревья, но зато с наизусть вызубренными знаниями об UB при записи по невалидному указателю или про исключения в деструкторах цепепе :-)

anonymous
()
Ответ на: комментарий от monk

Сможешь доказать, что он exception-safe?

а что с кодом Qt не так?

и зачем вообще доказывать что он exception-safe, если код, использующий Qt вообще исключения не проверяет?

i36_zubov
()
Ответ на: комментарий от monk

а если класс «сложный», из нескольких классов составленный?

class A {}
class B : public A {}
class C : public virtual A {}
class D : public B, public C {}

допустим ошибка в деструкторе A или B

D уже убит. vtable от него невалиден. деструктор D перезапустить нельзя, так как уже убит C.

вообще о чем весь этот спор? C++ сделан так, чтоб код работал быстро. хочешь сделать безопасно - сделай сам, напиши метод какой нибудь или класс. с рукожопами нянчиться никто не будет.

можно например на variadic templates собрать над сборищем классов обертку с обходом всех вложенных классов и вызовом функции. я так делал и оно работает. это как пример, возможны и другие варианты.

а хотелки каких то анонимных рукожопов приличных людей вообще не волнуют. не нравится - пусть пишут на жабе.

i36_zubov
()
Ответ на: комментарий от Gorthauer

eao197 выше указывал на то, что при добавлении в вектор элементов может возникнуть исключение bad_alloc. Да и вообще не понятно что делать с подобной информацией и зачем она кому-то может понадобиться на практике: файл должен закрываться, если он не закрывается, то что-то не так с вашей ОС или использованием API.

asaw ★★★★★
()
Ответ на: комментарий от i36_zubov

а что с кодом Qt не так?

Мнение эксперта такое: «Сложный объект, вроде QWidget-а, должен быть агрегатом из более простых объектов, каждый из которых в своем деструкторе чистит свою часть. Тогда не придется ваять деструкторы в виде простыней на три экрана.» :-)

anonymous
()
Ответ на: комментарий от eao197

Как-то так видимо)))

class invalid_user : public std::runtime_error {
  std::string username_;
  std::string password_;
public :
  invalid_user(std::string w, std::string n, std::string p)
    try : std::runtime_error(move(w)), username_(move(n)), password_(move(p))
  {}
  catch (...)
  {    
  };
  
  ~invalid_user() try
  {
  }
  catch (...)
  {    
  };

  const std::string & username() const { return username_; }
  const std::string & password() const { return password_; }
};

Хотя я не видел чтобы кому-то в здравом уме приходило в голову писать такие конструкции. Мне по крайней мере в прикладных задачах это никогда в жизни не пригождалось.

asaw ★★★★★
()
Ответ на: комментарий от asaw

Ну да, что-то вроде.

Только вот это прямой путь к тому, чтобы из монстра сделать еще более жуткого монстра. А все потому, что люди, привыкшие к более безопасным средам, не могут привыкнуть к тому, что и как делается в C++. Поэтому и начинается, «а вот я не могу в C++ как в Lisp-е, а вот сделайте мне вот так».

eao197 ★★★★★
()
Ответ на: комментарий от anonymous

Ха. Ха-ха-ха. UB.

Не тупи. e в данном случае не массив (за пределы которого можно было бы выйти), а указатель на область памяти, заканчивающуюся нулями.

monk ★★★★★
()
Ответ на: комментарий от eao197

Это будет уже другой C++

С++ с отлавливаемыми исключениями в деструкторе уже другой. По определению. Ладно, для совместимости пусть будет:

catch (exception &e) { /* поймали exception */ }
catchmany (exception *e) { /* поймали много exception */ }

То, что вы написали уже есть и работает.

Сейчас не работает. Исключение при выходе из деструктора обвалит программу.

исключения из деструкторов invalid_user::username_ и invalid_user::password_ перехватываются и обрабатываются до вызова деструктора std::runtime_error

Если обязательно обрабатываются до вызова, то тогда их ловить можно только внутри деструктора invalid_user. Иначе они просто попадут в список исключений.

деструктор invalid_user выпустит исключение. До вызова b(). Именно эту ситуацию и требуется обработать, чтобы b() таки была вызвана.

a();
try {
try { ... }
catch( const invalid_user & x ) {...}
} catch (...) { обработать ошибку invalid_user }
b();
monk ★★★★★
()
Последнее исправление: monk (всего исправлений: 1)
Ответ на: комментарий от eao197

Полагаю, что не то: http://stackoverflow.com/questions/2762078/why-is-c0xs-noexcept-checked-dynam...

Тут почти то же самое: совместимость. Хотя она и так сломана, так как в нынешнем C++ ещё работает:

$ cat test.cpp
#include <iostream>

class A
{
public:
  ~A() { throw 1; };
};

int main()
{
  try {
  A a;
  } catch (...) { std::cout << "ok\n"; }
}
$ ./a.out
ok

Ну и «I spoke to a standards committe member in person about this and he says that this was a rushed decision, motivated mainly by a constraint on move operations in containers (you might otherwise end up with missing items after a throw, which violates the basic guarantee). Mind you, this is a man whose stated design philosophy is that fault intolerant code is good. Draw your own conclusions.». Транзакции на move вполне были возможны, просто не захотели заморачиваться: fault intolerant code is good.

monk ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.