异常抛出时析构函数未被调用

6
请考虑以下代码:
#include <iostream>
using namespace std;

class Test {
  static int count;
  int id;
public:
  Test() {
    count++;
    id = count;
    cout << "Constructing object number " << id << endl;
    if(id == 4)
       throw 4;
  }
  ~Test() { cout << "Destructing object number " << id << endl; }
};

int Test::count = 0;

int main() {
  try {
    Test array[5];
  } catch(int i) {
    cout << "Caught " << i << endl;
  }
}

上面的代码将产生以下输出:
Constructing object number 1
Constructing object number 2
Constructing object number 3
Constructing object number 4
Destructing object number 3
Destructing object number 2
Destructing object number 1
Caught 4

我以为当对象超出范围时,析构函数总是会被调用,即使抛出异常也是如此。为什么在这种情况下没有调用一个 Test 实例的析构函数呢?


我已经修改了你的问题,使其更适合在Stack Overflow上发布。请遵循问题指南以供将来参考,否则你的问题可能会被投票降低或关闭。 - In silico
2个回答

8
您正在创建一个由5Test对象组成的数组,但是在创建3完整的对象后抛出异常,该异常在第4个对象的构造函数中抛出。直到构造函数的结束括号被执行,第4个对象的构造才算完成。
堆栈展开时,按照它们被创建的相反顺序调用已经完全构造的那些3个对象的析构函数,因为第4和第5个对象从未被构造,所以它们的析构函数从未被调用。
异常规则如下:
一旦抛出异常,将调用该范围内所有已完全创建对象的析构函数。
完全创建的对象是指其构造函数已经干净地调用而没有任何异常。

这个 每周大师文章 (#66) 对于这种情况特别相关。 - In silico
差一 -- 只有3个完全构造的对象,因此只调用了3个析构函数。 - Chris Dodd

3

以下是在id = count之后编写cout语句的示例:

相反,您可以按照以下方式编写cout语句:
    id = count;
    cout << "Constructing object number " << id << endl;
    if(id == 4)
    throw 4;

你应该在throw语句后面进行编写。这样会更好地了解发生了什么。像这样:

Test() {
count++;
id = count;
if(id == 4)
   throw 4;
 cout << "Constructing object number " << id << endl;
     }

输出结果应该是: 构造对象编号1 构造对象编号2 构造对象编号3 销毁对象编号3 销毁对象编号2 销毁对象编号1 捕获4

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接