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

Является ли деструктор вызываемым, если конструктор генерирует исключение?

Ищем ответ для С# и С++. (в С#, замените 'destructor' на 'finalizer')

4b9b3361

Ответ 1

Преамбула: У Травы Саттера есть отличная статья по теме:

http://herbsutter.wordpress.com/2008/07/25/constructor-exceptions-in-c-c-and-java/

С++: Да и Нет

Пока объект-деструктор не будет вызываться, если его конструктор выбрасывает (объект "никогда не существовал" ), можно было бы назвать деструкторы его внутренних объектов.

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

Например:

struct Class
{
   Class() ;
   ~Class() ;

   Thing *    m_pThing ;
   Object     m_aObject ;
   Gizmo *    m_pGizmo ;
   Data       m_aData ;
}

Class::Class()
{
   this->m_pThing = new Thing() ;
   this->m_pGizmo = new Gizmo() ;
}

Порядок создания будет:

  • m_aObject будет иметь свой конструктор.
  • m_aData будет иметь свой конструктор.
  • Конструктор классов называется
  • Конструктор внутреннего класса, m_pThing будет иметь свой новый и последующий конструктор.
  • Конструктор внутреннего класса, m_pGizmo будет иметь свой новый и последующий конструктор.

Скажем, мы используем следующий код:

Class pClass = new Class() ;

Некоторые возможные случаи:

  • Если m_aData бросает при построении, m_aObject будет вызван своим деструктором. Затем освобождается память, выделенная "новым классом".

  • Если m_pThing throw в новом Thing (из памяти), m_aData, а затем m_aObject будет вызван их деструкторами. Затем освобождается память, выделенная новым классом.

  • Если m_pThing бросается при построении, память, выделяемая "новой вещью", будет освобождена. Затем m_aData, а затем m_aObject вызовет их деструкторы. Затем освобождается память, выделенная новым классом.

  • Если m_pGizmo бросает при построении, память, выделенная "новым Gizmo", будет освобождена. Затем m_aData, а затем m_aObject вызовет их деструкторы. Затем память, выделенная новым классом, освобождается. Обратите внимание, что m_pThing просочился

Если вы хотите предложить гарантию Basic Exception, вы не должны течь, даже в конструкторе. Таким образом, вам придется писать это так (используя STL или даже Boost):

struct Class
{
   Class() ;
   ~Class() ;

   std::auto_ptr<Thing>   m_pThing ;
   Object                 m_aObject ;
   std::auto_ptr<Gizmo>   m_pGizmo ;
   Data                   m_aData ;
}

Class::Class()
   : m_pThing(new Thing())
   , m_pGizmo(new Gizmo())
{
}

Или даже:

Class::Class()
{
   this->m_pThing.reset(new Thing()) ;
   this->m_pGizmo.reset(new Gizmo()) ;
}

если вы хотите/должны создавать эти объекты внутри конструктора.

Таким образом, независимо от того, куда бросает конструктор, ничего не будет пропущено.

Ответ 2

Это для С# (см. код ниже), но не для С++.

using System;

class Test
{
    Test()
    {
        throw new Exception();
    }

    ~Test()
    {
        Console.WriteLine("Finalized");
    }

    static void Main()
    {
        try
        {
            new Test();
        }
        catch {}
        GC.Collect();
        GC.WaitForPendingFinalizers();
    }
}

Отпечатает "Завершено"

Ответ 3

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

Тем не менее, деструктор его базового класса (если есть) вызван, потому что объект был создан как объект базового класса.

Кроме того, любые переменные-члены также будут иметь свои деструкторы (как отмечали другие).

Примечание: это относится к С++

Ответ 4

В С++ ответ отрицательный - деструктор объекта не вызывается.

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

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

Ответ 5

Если конструктор не завершит выполнение, объект не существует, поэтому его не удастся уничтожить. Это в С++, я понятия не имею о С#.

Ответ 6

С++ -

Неа. Деструктор не вызывается для частично сконструированных объектов. Оговорка: деструктор будет вызван для его объектов-членов, которые полностью построены. (Включает автоматические объекты и собственные типы)

BTW - То, что вы действительно ищете, называется "Stack Unwinding"

Ответ 7

Не делайте то, что вызывает исключения в конструкторе.

Вызовите Initialize() после конструктора, который может генерировать исключения.

Ответ 8

Для С++ это рассматривается в предыдущем вопросе: Будет ли приведенный ниже код причиной утечки памяти в С++

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