Деструктор вызывается после броска из конструктора


я раньше думал, что в C++, если конструктор выдает исключение, деструктор этого "частично построенного" класса не вызывается.

но кажется, что это уже не так в C++11: я скомпилировал следующий код с g++ и он печатает"X destructor" в консоли. Почему это так?

#include <exception>
#include <iostream>
#include <stdexcept>
using namespace std;

class X
{
public:
    X() : X(10)
    {
        throw runtime_error("Exception thrown in X::X()");    
    }
    X(int a)
    {
        cout << "X::X(" << a << ")" << endl;
    }
    ~X()
    {
        cout << "X destructor" << endl;
    }
};

int main()
{
    try
    {
        X x;
    }
    catch(const exception& e)
    {
        cerr << "*** ERROR: " << e.what() << endl;
    }
}

выход

Standard out:
X::X(10) 
X destructor
Standard error: 
*** ERROR: Exception thrown in X::X()
2 72

2 ответа:

делегирование конструкторов-это действительно новая функция, которая вводит новую логику разрушения.

давайте вернемся к продолжительность жизни объекта: время жизни объекта начинается, когда некоторые конструктор закончил. (См. 15.2/2. Стандарт называет это "главным конструктором".) В вашем случае, это конструктор X(int). Второй, делегирующий конструктор X() теперь действует как простая функция-член. После разматывания области действия, деструкторы все полностью построенные объекты называются, и это включает x.

последствия этого на самом деле довольно глубоки: теперь вы можете поместить "сложные" рабочие нагрузки в конструктор и в полной мере использовать обычное распространение исключений, если вы делегируете свой конструктор другому конструктору. Такая конструкция может устранить необходимость в различных"init" -функциях, которые раньше были популярны, когда не хотелось слишком много работать в обычном режиме конструктор.

конкретный язык, который определяет поведение, которое вы видите:

[C++11: 15.2/2]:[..] аналогично, если конструктор без делегирования для объекта после завершения выполнения и делегирования конструктор для этого объекта завершает работу с исключением, деструктор объекта будет вызван. [..]

я раньше думал, что в C++, если конструктор выдает исключение, деструктор этого "частично построенного" класса не вызывается.

но кажется, что это больше не так в C++11

это все еще правда. Ничего не изменилось с C++03 (для некоторого значения ничего ; -))

то, что вы думали, все еще верно, но нет частично построенного объекта при возникновении исключения.

в C++03 TC1 стандарт говорит (акцент мой):

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

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

принципиально то же самое правило применяется в C++11: как только X(int) возвратился, объект "конструктор завершил выполнение", поэтому он полностью построен, и поэтому его деструктор будет запущен в соответствующее время (когда он выходит из области действия или исключение выбрасывается на более позднем этапе его построения.) Это все то же правило, по сути.

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

class X
{
public:
    X(int a)
    {
        cout << "X::X(" << a << ")" << endl;
    }
    ~X()
    {
        cout << "X destructor" << endl;
    }
};

class X_delegating : X
{
public:
    X_delegating() : X(10)
    {
        throw runtime_error("Exception thrown in X::X()");    
    }
};

это не действительно как это, есть только один тип, но он аналогичен в том, как X(int) запускается конструктор, затем дополнительный код в делегировании конструктор запускается, и если это бросает X" базовый класс " (который на самом деле не является базовым классом) уничтожается.