寻找C#和C++的答案。(在C#中,用'finalizer'来替换'destructor')
寻找C#和C++的答案。(在C#中,用'finalizer'来替换'destructor')
以下代码可以在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/
如果一个对象的构造函数抛出异常(对象“不存在”),则其析构函数不会被调用,但其内部对象的析构函数可能会被调用。
总之,对象的所有内部部分(即成员对象)将按照它们的构造顺序的相反顺序调用其析构函数。除非以某种方式使用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() ;
}
创建顺序将是:
假设我们正在使用以下代码:
Class pClass = new Class() ;
一些可能的情况:
如果在构造函数中m_aData抛出异常,那么m_aObject的析构函数将会被调用。然后,由“new Class”分配的内存将被释放。
如果在new Thing(内存不足)时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 ++。
new T
初始化的T*
成员,那么你实际上有两个对象:新的T和T*
本身。就像int
一样,T*
没有析构函数。因此,当删除T*
成员时,指向的T并不会被删除。 - MSalters对于C ++,这在先前的问题中得到了解决:下面的代码会导致C++内存泄漏吗?
由于在C ++中,当构造函数中抛出异常时,析构函数不会被调用,但已构造的对象成员的dtors确实会被调用,这是使用智能指针对象而不是原始指针的主要原因 - 它们是防止此类情况下的内存泄漏的好方法。
C++ -
不是的。对于部分构造的对象,析构函数不会被调用。一个注意点:对于完全构造的成员对象,析构函数会被调用。(包括自动对象和本地类型)
顺便说一句 - 你真正要找的是所谓的“栈展开”。
不要在构造函数中执行可能引发异常的操作。
在构造函数之后调用一个可以抛出异常的Initialize()方法。