为什么在插入析构函数时总是得到“在抛出…的实例后终止调用?”

问题描述 投票:6回答:2

我正在尝试编写一个单元测试,以检测对类的lock()功能的无效使用。为此,我想使用析构函数并从那里引发异常。不幸的是,g ++并没有捕获异常,而是决定调用std :: terminate()。

有一个非常简化的类:

class A
{
public:
    A() : f_lock(0) {}
    ~A() { if(f_lock) throw my_exception("still locked"); }
    lock() { ++f_lock; }
    unlock() { --f_lock; }
private:
    int f_lock;
};

有一个有效的测试:

A *a = new A;
a->lock();
...
a->unlock();
delete a;

我正在尝试编写无效的测试:

A *a = new A;
a->lock();
...
bool success = false;
try
{
    delete a;
}
catch(my_exception const&)
{
    success = true;
}
catch(...)
{
    // anything else is a failure
}
if(!success)
{
    // test failed...
    CPPUNIT_ASSERT(!"test failed");
}

现在,即使在另一个异常处于活动状态时未调用throw,delete也会调用std::terminate()。 (即std::uncaught_exception()为假。)我也很清楚地捕获了所有异常!

我是不是做错了,还是g++被编程为总是在析构函数中这样做?


更新:

dyp在下面的评论中的答案有效!以下内容不直接调用std::terminate()

    ~A() noexcept(false) { throw ...; }

也作为您为什么不希望在析构函数中抛出异常的参考,该页面非常好;

https://www.securecoding.cert.org/confluence/display/cplusplus/ERR33-CPP.+Destructors+must+not+throw+exceptions


为澄清起见,有完整版本的析构函数。如我们所见,我首先发布了一条消息(它通常在您的控制台中,也可能会在日志中显示)。其次,我确保我们还没有处理异常。最后,我引发了一个名为exception_exit的异常,尽管在GUI应用程序中您可能希望显示某种MessageBox以便让用户知道发生了什么(因为您可以捕获),所以该异常会强制执行terminate()。消息,您可以将其显示给用户),然后强制关闭应用程序。

Node::~Node() noexcept(false)
{
    if(f_lock > 0)
    {
        // Argh! A throw in a destructor... Yet this is a fatal
        // error and it should never ever happen except in our
        // unit tests to verify that it does catch such a bug
        Message msg(message_level_t::MESSAGE_LEVEL_FATAL, err_code_t::AS_ERR_NOT_ALLOWED);
        msg << "a node got deleted while still locked.";

        // for security reasons, we do not try to throw another
        // exception if the system is already trying to process
        // an existing exception
        if(std::uncaught_exception())
        {
            // still we cannot continue...
            std::abort();
        }

        throw exception_exit(1, "a node got deleted while still locked.");
    }
}

此外,另一个细节,您应该使用NodeLock对象来管理f_lock标志。这是异常安全的,因为它使用RAII(即作用域锁)。但是,在这一点上,我不想强​​迫用户使用NodeLock来锁定/解锁节点,因此在析构函数中进行此测试。

c++ exception g++ destructor throw
2个回答
4
投票

在C ++ 11中,添加了noexcept关键字。可以在函数异常规范中使用:


1
投票
http://www.parashift.com/c++-faq/dtors-shouldnt-throw.html上有详细说明。比我自己的话更好的解释:)

摘录:

您可以在析构函数中引发异常,但是该异常不能离开析构函数;如果析构函数通过发出异常退出,则可能会发生各种不良情况,因为将违反标准库的基本规则和语言本身。不要这样做
© www.soinside.com 2019 - 2024. All rights reserved.