是在构造函数引发异常时调用的析构函数

Is the destructor called if the constructor throws an exception?

本文关键字:调用 析构函数 异常 构造函数      更新时间:2023-10-16

正在寻找C#和C++的答案。(在C#中,将"析构函数"替换为"终结器")

它适用于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();
    }
}

这将打印"Finalized"

序言:Herb Sutter有一篇关于的伟大文章

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抛出新的Thing(内存不足),m_aData,然后m_aObject将调用它们的析构函数。然后,由新类分配的内存被释放。

  • 如果m_pThing在构造时抛出;"新事物";将被解除分配。然后m_aData和m_aObject将调用它们的析构函数。然后,由新类分配的内存被释放。

  • 如果m_pGizmo在构造时抛出;新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())
{
}

甚至:

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

如果您希望/需要在构造函数中创建这些对象。

这样,无论构造函数抛出到哪里,都不会泄露任何内容。

仍在构造的类的析构函数不会被调用,因为对象从未完全构造好。

然而,它的基类(如果有的话)的析构函数被调用了,因为该对象是作为基类对象构造的。

此外,任何成员变量都会调用其析构函数(正如其他人所指出的)。

注意:这适用于C++

在C++中,答案是no-对象的析构函数被不调用

但是,对象上任何成员数据的析构函数都将被调用,除非在构造其中一个时抛出异常

C++中的成员数据以与声明相同的顺序初始化(即构造),因此当构造函数抛出时,所有已初始化的成员数据(无论是在成员初始化列表(MIL)中还是在其他情况下)都将以相反的顺序再次被拆下。

如果构造函数没有完成执行,那么对象就不存在,因此没有什么可销毁的。这是在C++中,我对C#一无所知。

对于C++,这在前面的问题中得到了解决:下面的代码会导致C++中的内存泄漏吗

由于在C++中,当在构造函数中抛出异常时,析构函数不会被调用,但对象成员(已构造)的dtor会被调用,这是使用智能指针对象而不是原始指针的主要原因-在这种情况下,它们是防止内存泄漏的好方法。

C++-

没有。不为部分构造的对象调用析构函数。洞穴:析构函数将为其完全构建的成员对象调用。(包括自动对象和本机类型)

BTW-您真正想要的是所谓的"堆栈放卷"

不要在构造函数中做导致异常的事情。

在可以引发异常的构造函数之后调用Initialize()。