Подтвердить что ты не робот

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

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

Но похоже, что это больше не так в С++ 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()
4b9b3361

Ответ 1

Делегирующие кондукторы - действительно новая функция, которая вводит новую логику уничтожения.

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

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

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

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

Ответ 2

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

Но похоже, что это не так в С++ 11

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

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

В стандарте С++ 03 TC1 говорится (основное внимание):

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

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

В принципе, такое же правило применяется в С++ 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 "базовый класс" ( который на самом деле не является базовым классом) уничтожается.