如果构造函数抛出异常,是否会调用析构函数?

Qwe*_*tie 42 c# c++ destructor finalizer

寻找C#和C++的答案.(在C#中,用'finalizer'替换'析构函数')

Jon*_*eet 54

它适用于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();
    }
}
Run Code Online (Sandbox Code Playgroud)

这打印"已完成"


pae*_*bal 51

序言:Herb Sutter有一篇关于这个主题的精彩文章:

http://herbsutter.wordpress.com/2008/07/25/constructor-exceptions-in-cc-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() ;
}
Run Code Online (Sandbox Code Playgroud)

创建顺序为:

  1. m_aObject将调用其构造函数.
  2. m_aData将调用其构造函数.
  3. 调用类构造函数
  4. 在Class构造函数中,m_pThing将调用其新的构造函数.
  5. 在Class构造函数中,m_pGizmo将调用其新的构造函数.

假设我们使用以下代码:

Class pClass = new Class() ;
Run Code Online (Sandbox Code Playgroud)

一些可能的情况:

  • 如果m_aData在构造时抛出,m_aObject将调用其析构函数.然后,取消分配由"new Class"分配的内存.

  • 如果m_pThing抛出新的Thing(内存不足),m_aData,然后m_aObject将调用它们的析构函数.然后,释放由新类分配的内存.

  • 如果m_pThing在构造时抛出,则"new Thing"分配的内存将被释放.然后m_aData,然后m_aObject将调用它们的析构函数.然后,释放由新类分配的内存.

  • 如果m_pGizmo在构造时抛出,"new Gizmo"分配的内存将被释放.然后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())
{
}
Run Code Online (Sandbox Code Playgroud)

甚至:

Class::Class()
{
   this->m_pThing.reset(new Thing()) ;
   this->m_pGizmo.reset(new Gizmo()) ;
}
Run Code Online (Sandbox Code Playgroud)

如果你想/需要在构造函数中创建这些对象.

这样,无论构造函数抛出何处,都不会泄露任何内容.


Mar*_*rkR 10

仍未构造的类的析构函数未被调用,因为该对象从未完全构造.

但是,它的基类(如果有的话)的析构函数被调用,因为该对象被构造为基类对象.

此外,任何成员变量也会调用它们的析构函数(正如其他人所指出的那样).

注意:这适用于C++