ria*_*mat 3 c++ exception-handling exception new-operator
我定义了一个类foo如下:
class foo {
private:
static int objcnt;
public:
foo() {
if(objcnt==8)
throw outOfMemory("No more space!");
else
objcnt++;
}
class outOfMemory {
public:
outOfMemory(char* msg) { cout << msg << endl;}
};
~foo() { cout << "Deleting foo." << endl; objcnt--;}
};
int foo::objcnt = 0;
Run Code Online (Sandbox Code Playgroud)
这是主要功能:
int main() {
try {
foo* p = new foo[3];
cout << "p in try " << p << endl;
foo* q = new foo[7];
}catch(foo::outOfMemory& o) {
cout << "Out-of-memory Exception Caught." << endl;
}
}
Run Code Online (Sandbox Code Playgroud)
很明显,行"foo*q = new foo [7];" 仅成功创建5个对象,并在第6个对象上抛出内存不足异常.但事实证明,只有5个析构函数调用,并且没有调用destrcutor来存储位于p指向的3个对象的数组.所以我想知道为什么?为什么程序只调用这5个对象的析构函数?
只对完全构造的对象调用析构函数 - 这些对象的构造函数正常完成.只有在进行中抛出异常时才会自动发生new[].因此,在您的示例中,将在运行期间为完全构造的五个对象运行析构函数q = new foo[7].
因为new[]对于p指向成功完成的数组,现在将数组处理到您的代码并且C++运行时不再关心它 - 除非您这样做,否则不会运行析构函数delete[] p.
"原子"C++分配和构造函数是正确且异常安全的:If new T; 投掷,没有任何泄漏,如果new T[N]在途中任何地方抛出,已经建造的所有东西都被摧毁.所以没什么可担心的.
现在是一个题外话:
您始终必须担心的是在任何一个责任单位中使用多个 new表达式.基本上,你必须将任何new表达视为一个需要被完全构造的,负责任的守护对象吸收的烫手山芋.
考虑new并new[]严格地作为库构建块:您永远不会在高级用户代码中使用它们(可能除了new构造函数中的单个代码之外),并且只在库类中使用它们.
以机智:
// BAD:
A * p = new A;
B * q = new B; // Ouch -- *p may leak if this throws!
// Good:
std::unique_ptr<A> p(new A);
std::unique_ptr<B> q(new B); // who cares if this throws
std::unique_ptr<C[3]> r(new C[3]); // ditto
Run Code Online (Sandbox Code Playgroud)
另外一点:标准库容器实现了类似的行为:如果你说resize(N)(增长),并且在任何构造期间发生异常,那么所有已经构造的元素都将被销毁.也就是说,resize(N)要么将容器增长到指定的大小,要么根本不增长.(例如,在GCC 4.6中,请参阅_M_fill_insert()in 的实现,以bits/vector.tcc获得异常检查范围构建的库版本.)