且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

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

更新时间:2023-01-12 15:41:57

从析构函数中抛出异常是危险的.
如果另一个异常已经在传播,则应用程序将终止.

Throwing an exception out of a destructor is dangerous.
If another exception is already propagating the application will terminate.

#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
";
    }

    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
";
    }

}

这基本上归结为:

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

Anything dangerous (i.e. that could throw an exception) should be done via public methods (not necessarily directly). The user of your class can then potentially handle these situations by using the public methods and catching any potential exceptions.

然后析构函数将通过调用这些方法来完成对象(如果用户没有明确这样做),但任何抛出的异常都会被捕获并丢弃(在尝试修复问题之后).

The destructor will then finish off the object by calling these methods (if the user did not do so explicitly), but any exceptions throw are caught and dropped (after attempting to fix the problem).

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

So in effect you pass the responsibility onto the user. If the user is in a position to correct exceptions they will manually call the appropriate functions and processes any errors. If the user of the object is not worried (as the object will be destroyed) then the destructor is left to take care of business.

std::fstream

std::fstream

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

The close() method can potentially throw an exception. The destructor calls close() if the file has been opened but makes sure that any exceptions do not propagate out of the destructor.

因此,如果文件对象的用户想要对与关闭文件相关的问题进行特殊处理,他们将手动调用 close() 并处理任何异常.另一方面,如果他们不关心,那么析构函数将负责处理这种情况.

So if the user of a file object wants to do special handling for problems associated to closing the file they will manually call close() and handle any exceptions. If on the other hand they do not care then the destructor will be left to handle the situation.

Scott Myers 在他的书Effective C++"中有一篇关于这个主题的优秀文章

Scott Myers has an excellent article about the subject in his book "Effective C++"

显然也在更有效的 C++"中
第 11 条:防止异常离开析构函数

Apparently also in "More Effective C++"
Item 11: Prevent exceptions from leaving destructors