Ответ 1
Нет, создание исключения - лучший способ сообщить об ошибке во время создания объекта. (Так как нет возвращаемого значения, нет другого пути, кроме создания безголового объекта, что является плохим стилем в C++.)
От самого человека Бьярна Страуструпа: http://www.stroustrup.com/bs_faq2.html#ctor-exceptions
Re: "Но мой деструктор не был назван"
На самом деле. В C++ говорится, что время жизни объекта начинается, когда конструктор завершается. И это заканчивается прямо тогда, когда деструктор вызывается. Если ctor выбрасывает, то dtor не вызывается.
(Но вызываются dtors любых объектов-переменных-членов, чьи ctor уже выполнялись до завершения до этого ctor.)
Вы должны проконсультироваться со стандартом или хорошим учебником для более подробной информации, особенно связано с тем, что происходит, когда наследование вовлечено. Как правило, деструкторы вызываются в обратном порядке построения.
Ваш вопрос о том, почему "~ B" не был вызван в вашем конкретном коде, это потому, что вы не уловили исключение в main. Если вы измените свой код так, чтобы main перехватывал исключение, будет вызван "~ B()". Но когда генерируется исключение, которое не имеет перехватчика, реализация может завершить программу без вызова деструкторов или уничтожения статически инициализированных объектов.
Ссылка в C++ 11 стандарте (выделено мной):
15.5.1 The std::terminate() function [except.terminate]
1 В некоторых ситуациях обработка исключений должна быть прекращена для менее тонких методов обработки ошибок.
...
2 В таких случаях вызывается std::terminate() (18.8.3). В ситуации, когда соответствующий обработчик не найден, определяется реализацией, будет ли стек разматываться перед вызовом std::terminate().
В качестве дополнительного примечания, в общем случае с gcc и clang, ~B
будет вызываться в любом случае в вашей примерной программе, в то время как с MSVC ~B
вызываться не будет. Обработка исключений сложна, и стандарт позволяет авторам компиляторов экспериментировать с ними и выбирать, какую реализацию они считают лучшей в этом отношении, но они не могут выбирать неопределенное поведение.
Если для вашей программы действительно важно, чтобы деструкторы вызывались даже в этом случае, то вам следует убедиться, что вы ловите исключения в main
, чтобы ваш код был переносимым (работающим на всех соответствующих компиляторах).