【问题标题】:destructor not called in case of exception with scoped_ptr在 scoped_ptr 异常的情况下不调用析构函数
【发布时间】:2012-10-04 22:43:52
【问题描述】:

我刚刚开始使用 c++ boost 库。我在很多地方读到,当使用 scoped_ptr 时,即使出现异常,对象也总是会被销毁。

They behave much like built-in C++ pointers except that they automatically delete the object pointed to at the appropriate time. Smart pointers are particularly useful in the face of exceptions as they ensure proper destruction of dynamically allocated objects.

我尝试了以下代码。

#include<boost/scoped_ptr.hpp>

class B
{
  public:
    B(){ std::cout<< "B constructor call\n"; }
    ~B(){ std::cout<<"B destructor call\n"; }
};

class A
{
  public:
  boost::scoped_ptr<B> b;
  A():b(new B())  
  {
    throw 1;
  }
};

int main()
{
    A a; return 0;
}

output:
B constructor call
terminate called after throwing an instance of 'int'
Aborted (core dumped)

没有调用 B 的析构函数。但是我使用了 scoped_ptr,所以它应该调用 B 的析构函数,还是我误解了 scoped_ptr 的使用。

但如果 a 用 try catch 包围它,则调用 B 的析构函数。

try{
  A a;
} catch( ... ) {
}

在这种情况下,将调用 A 的析构函数,因为在 try 块中出现异常的情况下,所有本地分配的对象都将从堆栈中删除,并且我将指针包裹在 scoped_ptr 的对象中,因此当作用域对象的析构函数破坏时最终是指针。 scoped_ptr 也很有用,因为我们不必显式删除分配的内存,否则我误解了 scoped_ptr 的描述。

如果使用 scoped_ptr 发生异常,我如何调用 B 类的析构函数

【问题讨论】:

    标签: c++ exception boost destructor scoped-ptr


    【解决方案1】:

    没有匹配的异常处理程序,所以直接调用std::terminate,在这种情况下没有展开堆栈。将try/catch 放入捕获intmain 中,您将看到您的析构函数调用,即使该处理程序重新抛出。

    C++11 §15.1/2:

    当抛出异常时,控制权被转移到最近的具有匹配类型的处理程序; “最近的”是指try 关键字后面的compound-statementctor-initializer 最近被控制线程输入但尚未退出的处理程序。

    和§15.3/9:

    如果没有找到匹配的处理程序,则调用函数std::terminate();在此调用 std::terminate() 之前是否展开堆栈是实现定义的。

    Online demo

    【讨论】:

    • 如果你尝试在你的catch块中输出,你甚至会看到你的析构函数在catch块执行之前被调用了。
    • 当您回答问题时,我正在编辑问题。并添加了有问题的东西。之前忘记提了
    • @mayurharne :如果这不能回答您的问题,那么我不知道您的问题是什么。 :-] 当抛出一个从未被捕获的异常时,不能保证堆栈展开,即不能保证任何活动对象在进程终止之前被销毁。如果您想在遇到未捕获的异常时强制堆栈展开,那么只需在 main 内捕获并重新抛出所有异常,以便在主线程上始终有一个匹配的处理程序:int main() { try { /* all real code */ } catch (...) { throw; } }
    • 除此之外捕获异常是没有其他方法可以隐式销毁 scoped_ptr。
    • @mayurharne : 不 - 除非堆栈被展开,否则 没有any 对象的析构函数调用,并且唯一的 portable 强制堆栈展开的方法是总是有一些匹配的异常处理程序。 (可能有不可移植的、特定于编译器的方法以不同的方式完成此任务,但这是一个不同的问题。
    【解决方案2】:

    C++ 在展开堆栈时销毁一个局部变量(从函数返回,使用return 关键字或异常),所以它应该看到一个销毁你的scoped_ptr。但是在您的特殊情况下,main 中发生了异常,因此 terminate 将在 C++ 展开堆栈之前被调用并终止您的程序。

    void test() {throw 1;}
    void main() {
        string sMain;
        test();
    }
    

    在上面的例子中,sMain 不会被销毁,因为异常导致调用terminate

    sMain constructed
    exception occurred: main has no where to go, and it has no handler to handle
        the exception, so it will call `terminate`, but wait we are still at `main`
        so we have no stack unwinding here and sMain will never destroyed!!
    

    【讨论】:

    • sTestsMain 都将被销毁,或者两者都不会。没有一种情况会被销毁,也不会发生——要么发生堆栈展开,要么不发生。
    猜你喜欢
    • 2011-03-11
    • 2013-11-07
    • 1970-01-01
    • 2023-04-04
    • 2021-06-19
    • 2012-07-03
    • 2022-10-15
    • 1970-01-01
    相关资源
    最近更新 更多