首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如果不应该在析构函数中抛出异常,如何处理其中的错误?

如果不应该在析构函数中抛出异常,如何处理其中的错误?
EN

Stack Overflow用户
提问于 2008-09-24 21:34:01
回答 16查看 140.7K关注 0票数 298

大多数人说,永远不要从析构函数中抛出异常,这样做会导致未定义的行为。Stroustrup指出,“向量析构函数显式地为每个元素调用析构函数。这意味着如果一个元素析构函数抛出,向量销毁就会失败……实际上没有什么好的方法来防止从析构函数引发的异常,因此库无法保证元素析构函数抛出时是否会抛出”(参见附录E3.2)。

这篇文章似乎不是这样说的--抛出析构函数或多或少是可以的。

因此,我的问题是--如果从析构函数中抛出会导致未定义的行为,那么如何处理析构函数期间发生的错误?

如果在清理操作中发生错误,您会忽略它吗?如果这是一个可能在堆栈中处理但在析构函数中不正确的错误,那么从析构函数中抛出异常不是很有意义吗?

显然,这类错误是罕见的,但有可能。

EN

回答 16

Stack Overflow用户

回答已采纳

发布于 2008-09-24 21:35:59

从析构函数中抛出一个异常是危险的。

如果另一个异常已经在传播,应用程序将终止。

代码语言:javascript
复制
#include <iostream>

class Bad
{
    public:
        // Added the noexcept(false) so the code keeps its original meaning.
        // Post C++11 destructors are by default `noexcept(true)` and
        // this will (by default) call terminate if an exception is
        // escapes the destructor.
        //
        // But this example is designed to show that terminate is called
        // if two exceptions are propagating at the same time.
        ~Bad() noexcept(false)
        {
            throw 1;
        }
};
class Bad2
{
    public:
        ~Bad2()
        {
            throw 1;
        }
};


int main(int argc, char* argv[])
{
    try
    {
        Bad   bad;
    }
    catch(...)
    {
        std::cout << "Print This\n";
    }

    try
    {
        if (argc > 3)
        {
            Bad   bad; // This destructor will throw an exception that escapes (see above)
            throw 2;   // But having two exceptions propagating at the
                       // same time causes terminate to be called.
        }
        else
        {
            Bad2  bad; // The exception in this destructor will
                       // cause terminate to be called.
        }
    }
    catch(...)
    {
        std::cout << "Never print this\n";
    }

}

这基本上可以归结为:

任何危险的事情(即可能抛出异常)都应该通过公共方法(不一定是直接的)来完成。然后,类的用户可以通过使用公共方法和捕捉任何潜在的异常来处理这些情况。

然后,析构函数将通过调用这些方法来结束对象(如果用户没有显式地调用这些方法),但是任何异常抛出都会被捕获并删除(在试图修复问题之后)。

因此,实际上,您将责任传递给用户。如果用户能够纠正异常,他们将手动调用适当的函数并处理任何错误。如果对象的用户不担心(因为对象将被销毁),那么析构函数将留给处理业务。

举个例子:

std::fstream

close()方法可能引发异常。如果已打开文件,则析构函数将调用close(),但确保任何异常都不会传播到析构函数之外。

因此,如果文件对象的用户希望对与关闭文件相关的问题进行特殊处理,他们将手动调用close()并处理任何异常。另一方面,如果他们不关心,那么破坏者将被留下来处理这种情况。

Scott在他的书“有效的C++”中有一篇关于这个主题的优秀文章。

编辑:

显然也出现在“更有效的C++”中

项目11:防止异常离开析构函数

票数 224
EN

Stack Overflow用户

发布于 2008-12-17 15:22:03

抛出析构函数会导致崩溃,因为这个析构函数可能被称为“堆栈展开”的一部分。堆栈展开是在抛出异常时发生的过程。在此过程中,所有自“尝试”后被推入堆栈的对象,直到抛出异常,都将终止->,它们的析构函数将被调用。在此过程中,另一个异常抛出是不允许的,因为不可能一次处理两个异常,因此,这将引发对abort()的调用,程序将崩溃,控件将返回到操作系统。

票数 68
EN

Stack Overflow用户

发布于 2008-09-24 21:42:05

关于从析构函数中抛出的问题,真正要问自己的问题是:“调用者能用它做什么?”实际上,除了这个例外,您还能做什么有用的事情来抵消由析构函数抛出造成的危险吗?

如果我销毁了一个Foo对象,而Foo析构函数抛出了一个异常,那么我可以合理地处理它吗?我可以记录它,也可以忽略它。就这样。我无法“修复”它,因为Foo对象已经消失了。最好的情况是,我记录异常并继续,就好像什么都没发生一样(或者终止程序)。这真的值得通过从析构函数中抛出而导致未定义的行为吗?

票数 20
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/130117

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档