我正在与一些同事讨论在动态分配的类中抛出异常时会发生什么。我知道malloc
被调用,然后是类的构造函数。构造函数永远不会返回,那么malloc
会发生什么?
考虑以下示例:
class B
{
public:
B()
{
cout << "B::B()" << endl;
throw "B::exception";
}
~B()
{
cout << "B::~B()" << endl;
}
};
void main()
{
B *o = 0;
try
{
o = new B;
}
catch(const char *)
{
cout << "ouch!" << endl;
}
}
malloced内存o
会发生什么,它会泄漏吗? CRT是否捕获构造函数的异常并释放内存?
干杯!
富
答案 0 :(得分:10)
致电
new B();
解决了两件事:
new (xxx) B()
的展示位置)如果构造函数抛出,则调用相应的运算符delete。相应删除是放置的情况是唯一一种在没有语法:: operator delete()的情况下调用放置删除操作符的情况。 delete x;
或delete[] x;
不会调用展示位置删除操作符,并且没有与新位置类似的语法来调用它们。
请注意,虽然B的析构函数将不被调用,但是在调用operator delete之前,已经构造的子对象(B的成员或B和基类)将被销毁。未调用的构造函数是B的构造函数。
答案 1 :(得分:6)
当从构造函数抛出异常时,new释放的内存被释放,但是不会调用类B的析构函数。
答案 2 :(得分:2)
在这种情况下,您的对象o实际上并未构造,并且释放了new分配的内存。因此,析构函数不会被调用。所以你不需要打电话:
delete o;
一个有趣的设计模式是RAII - 资源获取是初始化。在此模式中,您使用构造函数来封装资源的获取,并在析构函数中释放资源。如果无法获取资源,则抛出构造函数 - 就像您的示例一样。因此,如果您有一个有效的对象,则拥有该资源。
如果构造了对象,那么您已成功获取资源。这意味着对于对象的生命,您拥有资源。删除对象后,将释放资源。如果永远不构造对象,那么您从未获得该资源。见维基百科:
http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization
答案 3 :(得分:1)
从C ++ 2003标准5.3.4 / 17 - 新增:
如果上述对象初始化的任何部分通过抛出异常终止并且可以找到合适的释放函数,则调用释放函数以释放构造对象的内存,之后异常继续传播在new-expression的上下文中。如果找不到明确的匹配解除分配函数,则传播异常不会导致释放对象的内存。 [注意:当被调用的分配函数不分配内存时,这是合适的;否则,很可能导致内存泄漏。 ]
所以可能有或没有泄漏 - 这取决于是否可以找到合适的解除分配器(通常情况下,除非操作符new / delete被覆盖)。如果有合适的解除分配器,如果构造函数抛出,编译器负责调用它。
请注意,这或多或少与构造函数中获取的资源发生的情况无关,这是我第一次尝试回答的问题 - 并且是许多常见问题解答,文章和帖子中讨论的问题。