C++ RAII无法正常工作?

Joh*_*ohn 13 c++ exception raii

我刚刚开始使用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;
}
Run Code Online (Sandbox Code Playgroud)

除了注释外我得到:

A 1 constructed
A 2 constructed
A 2 destructed
A 1 destructed
Run Code Online (Sandbox Code Playgroud)

正如所料,但除了我得到:

A 1 constructed
A 2 constructed
terminate called after throwing an instance of 'std::exception'
  what():  std::exception
Aborted
Run Code Online (Sandbox Code Playgroud)

所以即使它们超出范围,我的物体也不会被破坏.这不是RAII的全部基础.

指针和更正非常感谢!

Aar*_*ela 20

您没有处理异常的处理程序.当发生这种情况时,标准说调用std :: terminate,然后调用abort.请参阅"C++编程语言"第3版第14.7节.

  • 好吧,可以使用它来将终止记录到特定文件或向某人发送电子邮件. (2认同)

jal*_*alf 17

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

然后,为什么变量不会超出范围,这有点道理.我们实际上没有离开宣布它们的范围.可以认为发生的事情与此相同:

int main(void) {
  A a1(1);
  A a2(2);
  std::terminate();
}
Run Code Online (Sandbox Code Playgroud)

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

  • 这可能很愚蠢,但也很务实.C++规范试图避免限制实现者.理想情况下,他们希望能够在*any*CPU和*any*OS上创建符合标准的C++实现.由于主要功能在某种意义上标志着操作系统和C++之间的界限,因此他们不想过多地考虑它.你是对的,在现实世界中,如果他们没有将这个特定的细节留给实现,那就太好了.我们现在知道了,但是在1998年语言被标准化时是否明显?他们不想把自己画成一个角落 (3认同)
  • 不傻 - 实用.强制总是调用析构函数(而不是构造函数)会限制EH和堆栈展开的实现方式. (2认同)

Edo*_* A. 6

你在main中有一个unhanded异常,这意味着一个终止的调用.试试这个:

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


}
Run Code Online (Sandbox Code Playgroud)

  • 你将不得不尝试{} catch()*某处*,否则当你到达最外层范围时你就会终止. (7认同)
  • 我认为main()是不同的.尝试将所有内容放在您从main()调用的函数中. (2认同)
  • 不是"总是",只在main()中.尝试将代码移动到单独的函数,并将异常处理程序保留在main中. (2认同)

Mar*_*ork 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.
    }
}
Run Code Online (Sandbox Code Playgroud)


Dav*_*ley 5

正如其他人所指出的,你有一个未被捕获的异常,它调用terminate().它是实现定义的(参见标准,15.3第9段和第15.5.1段第2段)是否在这种情况下调用析构函数,并且实现中的定义显然是"否,它们不会".(如果因抛出没有处理程序的异常而调用terminate(),则不会调用析构函数.)