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

поиск ответа для C# и c++. (в C# замените "деструктор" на "финализатор")

8 ответов


преамбула: Херб Саттер имеет большую статью на эту тему:

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

C++: да и нет

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

в качестве резюме каждая внутренняя часть объекта (т. е. объекты-члены) будет иметь свои деструкторы вызываются в обратном порядке их строительства. Каждая вещь, построенная внутри конструктора, не будет иметь своего деструктора, если 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() ;
}

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

  1. m_aobject будет иметь свой конструктор.
  2. m_adata будет иметь свой конструктор.
  3. конструктор класса называется
  4. внутри конструктора класса m_pThing будет иметь его новый, а затем конструктор называется.
  5. внутри конструктора класса m_pGizmo будет иметь свой новый, а затем конструктор.

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

Class pClass = new Class() ;

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

  • если m_adata бросит на строительство, m_aobject будет иметь свой деструктор. Затем память, выделенная "новым классом", освобождается.

  • должен ли m_pthing бросить на новую вещь (из памяти), m_aData, а затем m_aobject будет вызывать свои деструкторы. Затем память, выделенная новым классом, освобождается.

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

  • должен ли m_pgizmo бросать на строительство, память, выделенную "new Подарок " будет освобожден. Затем m_adata, а затем m_aObject будут вызывать свои деструкторы. Затем память, выделенная новым классом, освобождается. обратите внимание, что m_pthing утечка

если вы хотите предложить основную гарантию исключения, вы не должны протекать, даже в конструкторе. Таким образом, вам придется написать это так (используя 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()) ;
}

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

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


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

using System;

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

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

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

это печатает "завершено"


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

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

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

NB: это относится к C++


в C++ ответом является деструктор no-объекта не называется.

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

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


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


C++ -

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

кстати-то, что вы действительно ищете, называется "размотка стека"


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

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


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

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