寻找C#和C ++的答案。 (在C#中,将'析构者'替换为'终结者')
答案 0 :(得分:51)
序言: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”分配的内存。
如果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())
{
}
甚至:
Class::Class()
{
this->m_pThing.reset(new Thing()) ;
this->m_pGizmo.reset(new Gizmo()) ;
}
如果你想/需要在构造函数中创建这些对象。
这样,无论构造函数在哪里抛出,都不会泄露任何内容。
答案 1 :(得分:49)
它适用于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();
}
}
这会打印“已完成”
答案 2 :(得分:10)
仍未构造的类的析构函数未被调用,因为该对象从未完全构造。
然而,调用其基类(如果有的话)的析构函数,因为该对象被构造为基类对象。
此外,任何成员变量也会调用它们的析构函数(正如其他人所指出的那样)。
注意:这适用于C ++
答案 3 :(得分:2)
在C ++中,答案是否定的 - 对象的析构函数不被调用。
但是,除非在构造它们之一时抛出异常,否则将调用对象上的任何成员数据的析构函数。
C ++中的成员数据按照声明的顺序进行初始化(即构造),因此当构造函数抛出时,所有已初始化的成员数据 - 显式地在成员初始化列表(MIL)中或其他 - 将以相反的顺序再次被拆除。
答案 4 :(得分:1)
如果构造函数没有完成执行,则该对象不存在,因此没有任何东西可以破坏。这是用C ++编写的,我不知道C#。
答案 5 :(得分:0)
C ++ -
不。不为部分构造的对象调用析构函数。警告:析构函数将被调用其完全构造的成员对象。 (包括自动对象和本机类型)
BTW - 你真正想要的是“堆栈展开”
答案 6 :(得分:0)
不要在构造函数中执行导致异常的事情。
在可以抛出异常的构造函数之后调用Initialize()。
答案 7 :(得分:0)
对于C ++,这在前一个问题中得到解决:Will the below code cause memory leak in c++
因为在C ++中,当构造函数中抛出异常时,析构函数不会被调用,但是对象的成员(已构造的)的dtors会被调用,这是使用智能指针对象而不是原始指针的主要原因 - 在这种情况下,它们是防止内存泄漏的好方法。