寻找 C# 和 C++ 的答案。(在 C# 中,将“析构函数”替换为“终结器”)
8 回答
它适用于 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-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() ;
}
创建顺序将是:
- m_aObject 将调用其构造函数。
- m_aData 将调用其构造函数。
- 类构造函数被调用
- 在 Class 构造函数中,m_pThing 将调用其 new 构造函数。
- 在 Class 构造函数中,m_pGizmo 将调用其 new 构造函数。
假设我们正在使用以下代码:
Class pClass = new Class() ;
一些可能的情况:
如果 m_aData 在构造时抛出,m_aObject 将调用其析构函数。然后,由“new Class”分配的内存被释放。
如果 m_pThing 抛出新事物(内存不足),m_aData,然后 m_aObject 将调用它们的析构函数。然后,由 new Class 分配的内存被释放。
如果 m_pThing 在构造时抛出,“new Thing”分配的内存将被释放。然后是 m_aData,然后是 m_aObject 将调用它们的析构函数。然后,由 new Class 分配的内存被释放。
如果 m_pGizmo 在构建时抛出,“new Gizmo”分配的内存将被释放。然后是 m_aData,然后是 m_aObject 将调用它们的析构函数。然后,由 new Class 分配的内存被释放。请注意,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++ 中,答案是否定的——不调用对象的析构函数。
但是,将调用对象上任何成员数据的析构函数,除非在构造其中一个时抛出异常。
C++ 中的成员数据按照其声明的顺序进行初始化(即构造),因此当构造函数抛出时,所有已初始化的成员数据——无论是在成员初始化列表 (MIL) 中显式地还是以其他方式——都将被拆除再次以相反的顺序。
如果构造函数没有完成执行,则该对象不存在,因此没有什么可破坏的。这是在 C++ 中,我不知道 C#。
对于 C++,这在上一个问题中得到了解决: 以下代码是否会导致 c++ 中的内存泄漏
由于在 C++ 中,当构造函数中抛出异常时,不会调用析构函数,但会调用对象成员(已构造的)的 dtor,这是在原始指针上使用智能指针对象的主要原因 - 它们是在这种情况下防止内存泄漏的好方法。
C++ -
没有。部分构造的对象不调用析构函数。一个警告:析构函数将被调用为其完全构造的成员对象。(包括自动对象和本机类型)
顺便说一句 - 你真正要找的是“堆栈展开”
不要做导致构造函数异常的事情。
在可以抛出异常的构造函数之后调用 Initialize()。