如果在构造 A 期间抛出异常,您的析构函数将not叫做。
显然,解决方案取决于您正在做什么,但理想情况下您不会have进行任何清理工作。你应该利用RAII http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization,你的班员应该自己清理。
也就是说,不要使用任何原始指针;把它们包起来,让包装纸来处理。惊喜! C++ 程序员和你一样讨厌内存管理。我们喜欢把它包起来然后忘记它。
不过,如果你确实需要的话,我认为这是常见的:
struct foo
{
int* i;
some_member_that_could_throw crap;
foo() // do *not* new i! if the second member throws, memory is leaked.
{ // rather:
// okay we made it, the other member must have initialized
i = new int;
}
};
对于您的指针,它的值保持不变。什么时候new
抛出异常(无论出于何种原因),堆栈将被展开。表达式的其余部分被放弃。
以下是异常和对象创建的工作原理。这是一个递归过程,因为每个成员或基类将依次遵循此列表。基本类型没有构造函数;这是递归的基本情况。
- 首先,构造我们的每个基类。 (反过来又运行这个列表。)
- 一一初始化类的成员。
- 运行构造函数主体。
- 以一个完全构建的对象完成。
显然,如果第 1 项失败,我们不需要做任何清理工作,因为我们的成员都没有被初始化。我们那里很好。
二是不同。如果在任何时候其中一个无法构造,则初始化的成员so far将被破坏,然后构造函数将停止进度,并且异常会以愉快的方式继续。这就是为什么当您让您的成员自行清理时,您无需担心。未初始化的无事可做,而已初始化的将运行其析构函数,并在其中进行清理。
三者更是如此。现在您的对象已完全初始化,您可以保证它们都将运行其析构函数。再说一次,把事情包起来,你就没什么可担心的了。However如果你有一个原始指针,那么就需要使用 try/catch 块:
try
{
// some code
}
catch (..) // catch whatever
{
delete myrawPointer; // stop the leak!
throw; // and let the exception continue
}
在没有 RAII 的情况下编写异常安全代码要混乱得多。