有人可以解释如果在堆上分配对象数组的过程中抛出异常会发生什么?

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个对象的析构函数?

sha*_*oth 6

只对完全构造的对象调用析构函数 - 这些对象的构造函数正常完成.只有在进行中抛出异常时才会自动发生new[].因此,在您的示例中,将在运行期间为完全构造的五个对象运行析构函数q = new foo[7].

因为new[]对于p指向成功完成的数组,现在将数组处理到您的代码并且C++运行时不再关心它 - 除非您这样做,否则不会运行析构函数delete[] p.

  • tl; dr*使用向量*. (3认同)

Ker*_* SB 6

"原子"C++分配和构造函数正确且异常安全的:If new T; 投掷,没有任何泄漏,如果new T[N]在途中任何地方抛出,已经建造的所有东西都被摧毁.所以没什么可担心的.

现在是一个题外话:

始终必须担心的是在任何一个责任单位中使用多个 new表达式.基本上,你必须将任何new表达视为一个需要被完全构造的,负责任的守护对象吸收的烫手山芋.

考虑newnew[]严格地作为库构建块:您永远不会在高级用户代码中使用它们(可能除了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获得异常检查范围构建的库版本.)