C# 如果构造函数引发异常,是否调用析构函数? 寻找C和C++的答案。(在C中,用‘终结器’替换‘析构函数’)

C# 如果构造函数引发异常,是否调用析构函数? 寻找C和C++的答案。(在C中,用‘终结器’替换‘析构函数’),c#,c++,destructor,finalizer,C#,C++,Destructor,Finalizer,它对C++是(参见下面的代码),而不是C++()。 using System; class Test { Test() { throw new Exception(); } ~Test() { Console.WriteLine("Finalized"); } static void Main() { try { new Test();

它对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();
    }
}

这将打印“已完成”

如果构造函数没有完成执行,则对象不存在,因此没有要销毁的内容。这是C++中的,我对C++的概念没有了解。

< P>C++-< 没有。对于部分构造的对象,不调用析构函数。警告:对于完全构造的成员对象,将调用析构函数。(包括自动对象和本机类型)


BTW——你真正想要的是C++中的“栈退绕”< /p> ,答案是“不”对象的析构函数不被调用。

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


C++中的成员数据按照声明的顺序初始化(即构造),因此当构造函数抛出所有已初始化的成员数据时,要么显式地初始化成员初始化列表(MIL)。否则-将以相反的顺序再次被拆除。

不要做会导致构造函数中出现异常的事情


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

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

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() ;
}
创建顺序为:

  • m_aObject将调用其构造函数
  • m_aData将调用其构造函数
  • 类构造函数被调用
  • 在类构造函数中,m_pThing将调用它的新构造函数,然后调用它的构造函数
  • 在类构造函数中,m_pGizmo将调用它的新构造函数,然后调用它的构造函数
  • 假设我们正在使用以下代码:

    Class pClass = new Class() ;
    
    一些可能的情况:

    • 如果m_aData抛出构造,m_AOObject将调用其析构函数。然后,释放“新类”分配的内存

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

    • 如果m_pThing抛出构造,则“新事物”分配的内存将被释放。然后m_aData,然后m_aObject将调用它们的析构函数。然后,释放新类分配的内存

    • 如果m_pGizmo抛出构造,则“新Gizmo”分配的内存将被释放。然后m_aData,然后m_aObject将调用它们的析构函数。然后,释放新类分配的内存注意,m_pThing泄漏了

    如果您想提供基本的异常保证,就不能泄漏,即使在构造函数中也是如此。因此,您必须这样编写(使用STL,甚至Boost):

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


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

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

    但是,将调用其基类(如果有)的析构函数,因为对象的构造方式与基类对象一样

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


    NB:这适用于C++的<+/p>

    C++,这在前面的问题中得到解决:


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

    我认为这里要做的事情是设置一个一次性项目(或两个:每个语言一个)并进行检查。从命令行编译的乐趣在于不需要设置项目。只需在一个文本编辑器中加载一个文件,编译就非常容易,并且运行:)@ LueC等,C++和C语言的语义有很大的不同。这是两个独立的问题。对于C++,这可能是正确的,但不是C++。我认为测试是C语言中的终结器,而不是析构函数。啊,我知道~语法是设置终结器的糖分。在我的ctor可能抛出的情况下,我希望手动添加终结器作为ctor的最后一步,以避免清理不存在的混乱。@Logan:这取决于您阅读的C#spec的哪个版本,他们称它为“终结器”还是“析构函数”。@Jon:除了Ecma-334,还有其他C#specification吗?我知道它们是存在的,事实上它们是在你的网站上发布的。。。太糟糕了,2.0以上的版本没有标准规范。那就是说(抗议)。。。C语言终结器的语义不像C++析构函数(很明显,你知道)。在C#示例中,我认为用终结器代替处理器更合适。@FernandoPelliccioni:不,没有比这更高版本的ECMA规范了。至于处置——这个问题专门询问终结器。将其更改为使用Disposition不会回答这个问题。实际上,我正在编写STL样式的集合,所以我手动调用构造函数和析构函数。因此,我想知道在发生异常时调用析构函数是否正常。但是你描述的这个“部分破坏”必须由编译器完成,所以我不必。请你解释一下为什么?
    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()) ;
    }