13

我刚刚开始使用 C++ 中的 RAII 并设置了一个小测试用例。要么我的代码很混乱,要么 RAII 不工作!(我猜是前者)。

如果我运行:

#include <exception>
#include <iostream>
class A {
public:
    A(int i) { i_ = i; std::cout << "A " << i_ << " constructed" << std::endl; }
    ~A() { std::cout << "A " << i_ << " destructed" << std::endl; }
private:
    int i_;
};

int main(void) {
    A a1(1);
    A a2(2);
    throw std::exception();
    return 0;
}

除了注释掉的例外,我得到:

A 1 constructed
A 2 constructed
A 2 destructed
A 1 destructed

正如预期的那样,但除了我得到:

A 1 constructed
A 2 constructed
terminate called after throwing an instance of 'std::exception'
  what():  std::exception
Aborted

所以我的对象即使超出范围也不会被破坏。这不是 RAII 的全部基础吗?

非常感谢指点和更正!

4

10 回答 10

20

您没有异常处理程序。当这种情况发生时,标准说 std::terminate 被调用,而后者又调用 abort。请参阅 The C++ Programming Language,第 3 版中的第 14.7 节。

于 2009-07-09T14:16:38.230 回答
17

问题是它main具有特殊的地位。当从那里抛出异常时,堆栈无法有意义地展开,应用程序只是调用std:terminate

然后,为什么变量不会超出范围就有点道理了。我们实际上并没有离开声明它们的范围。发生的事情可以被认为等同于:

int main(void) {
  A a1(1);
  A a2(2);
  std::terminate();
}

(我相信在这种情况下是否调用析构函数是实现定义的,所以在某些平台上,它会按您的预期工作)

于 2009-07-09T14:18:24.700 回答
6

您在 main 中有一个未经处理的异常,这意味着调用终止。尝试这个:

int main(void)
{
    try
    {
        A a1(1);
        A a2(2);
        throw std::exception();
        return 0;
    }
    catch(const std::exception & e)
    {
        return 1;
    }


}
于 2009-07-09T14:13:08.513 回答
6

如果异常从 main() 中逃脱,则它是实现定义的天气堆栈被展开。

尝试

int main()
{
    try
    {
        doWork(); // Do you experiment here. 
    }
    catch(...)
    {   /*
         * By catching here you force the stack to unwind correctly.
         */
        throw;  // re-throw so exceptions pass to the OS for debugging.
    }
}
于 2009-07-09T16:53:01.317 回答
5

正如其他人指出的那样,您有一个未捕获的异常,它调用了 terminate()。在这种情况下是否调用析构函数是实现定义的(参见标准,15.3 第 9 段和 15.5.1 第 2 段),并且您的实现中的定义显然是“不,它们不会”。(如果由于抛出没有处理程序的异常之外的任何其他原因调用了 terminate(),则不会调用析构函数。)

于 2009-07-09T14:23:11.743 回答
4

您的 A 对象没有被销毁,因为正在调用 std::terminate 。

当未处理的异常从 main 泄漏时调用 std::terminate。如果您将代码包装在 try/catch 中(即使 catch 只是重新引发),您将看到您所期望的行为。

于 2009-07-09T14:26:18.993 回答
3

您没有正确处理异常,因此您的应用程序在对象超出范围之前退出。

我将再解释一下。如果异常“冒泡”到主堆栈,则展开(编辑)。即使将代码移动到辅助函数也无法解决此问题。前任:

      1 #include <exception>
      2 #include <iostream>
      3
      4 void test();
      5
      6 class A {
      7     public:
      8         A(int i) { i_ = i; std::cout << "A " << i_ << " constructed" << std::endl; }
      9         ~A() { std::cout << "A " << i_ << " destructed" << std::endl; }
     10     private:    int i_;
     11 };
     12
     13
     14 int main(void) {
     15     test();
     16     return 0;
     17 }
     18
     19 void test(){
     20             A a1(1);
     21             A a2(2);
     22            throw std::exception();
     23 }

上面的代码不能解决问题。解决此问题的唯一方法是将抛出的异常包装在 try-catch 块中。这将阻止异常到达主对象,并在对象超出范围之前停止正在发生的终止。

于 2009-07-09T14:15:50.757 回答
1

其他人建议在里面放一个 try/catchmain()来处理这个问题,效果很好。出于某种原因,我发现很少使用的“function-try-block”看起来更好,这让我感到惊讶(我认为它看起来太奇怪了)。但我认为没有任何真正的优势:

int main(void) 
try
{
    A a1(1);
    A a2(2);
    throw std::exception();
    return 0;
}
catch (...) 
{
    throw;
}

一些缺点是,由于它很少使用,很多开发人员在看到它时就会陷入循环,如果这是一个考虑因素,VC6 就会扼杀它。

于 2009-07-10T02:39:17.167 回答
0

以下代码有效。

#include <exception> 
#include <iostream> 

class A { 
public: 
    A(int i) { i_ = i; std::cout << "A " << i_ << " constructed" << std::endl; } 
    ~A() { std::cout << "A " << i_ << " destructed" << std::endl; } 
private: 
    int i_; 
}; 

void test() {
    A a1(1); 
    A a2(2); 
    throw std::exception(); 
} 

int main(void) { 
 try {
  test();
 } catch(...) {
 }
    return 0; 
} 
于 2010-02-05T09:22:45.460 回答
0

由于异常在到达 main() 时未处理,因此会导致调用 std::terminate(),您基本上相当于

int main(void) {
  A a1(1);
  A a2(2);
  exit(1);
}

如果程序在超出范围之前终止,则不保证调用析构函数。对于 RAII 中的另一个漏洞,请考虑:

int main(void) {
  A *a1 = new A(1);
}
于 2009-07-09T17:04:28.033 回答