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

亚历克西斯·威尔克

我正在尝试编写一个单元测试,以检测对类的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,它会强制执行一个异常terminate()尽管在GUI应用程序中,您可能希望显示某种MessageBox,以使用户知道发生了什么事情(因为您可以捕获Message,因此可以向用户显示该消息。 ),然后强制关闭应用程序。

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来锁定/解锁节点,因此在析构函数中进行了此测试。

MM

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

  • noexcept(true)与相同throw(),即terminate如果抛出任何内容,则此函数
  • noexcept(false) 表示函数可能会抛出任何东西

对于大多数函数,除非您为它们指定一个,否则它们没有异常规范没有异常规范的函数可能会抛出任何异常

不过,在C ++ 11 [class.dtor] / 3中发现了析构函数的特殊情况:

不具有异常规范的析构函数的声明被隐式认为具有与隐式声明(15.4)相同的异常规范

引用的规则15.4说,隐式声明的特殊成员函数始终具有异常规范该规范由以下规则确定,[except.spec] / 14:

隐式声明的特殊成员函数(第12条)应具有异常规范如果f是隐式声明的默认构造函数,副本构造函数,move构造函数,析构函数,副本赋值运算符或move赋值运算符,则当且仅当直接由函数异常规范允许时,其隐式异常规范才指定type-id的隐式定义调用如果它直接调用的任何函数不允许所有异常,则应允许所有异常;如果它直接调用的每个函数不允许任何异常则应不允许任何异常。TTfff

此子句中的“直接调用的函数”是指任何成员变量或基类的析构函数(递归应用)。如果没有这样的函数,则该函数不会允许任何例外,因此默认值为noexcept(true)

我们可以像这样总结以上引用中与您的代码有关的部分:

  • 如果所有子对象都没有析构函数,或者没有隐式生成的析构函数,或者没有声明为noexcept(true)或等效的析构函数那么此类的析构函数默认为noexcept(true)

因此,将析构函数更改为拥有的noexcept(false)将重现C ++ 03的行为。


在C ++ 03中,这些都不会出现,并且您的析构函数将默认允许所有异常。我不确定为什么要在C ++ 11中进行此更改,但这可能是因为除非您真的知道自己在做什么,否则从析构函数中抛出它是一个坏主意。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

在抛出 std::exception 实例后调用终止

为什么在抛出我的析构函数时没有调用 std::terminate

在抛出'kj :: ExceptionImpl'实例后,Cap'n Proto终止被调用

抛出'std :: regex_error'实例后调用终止

抛出异常实例后终止调用,核心转储

抛出'pqxx :: broken_connection'实例后调用终止

在抛出“std::system_error”实例后调用 Tensorflow 终止

抛出'std :: out_of_range'实例后调用终止

为什么在抛出'std :: bad_alloc'实例后终止调用?

得到错误“抛出'std :: bad_alloc'what():whatstd :: bad_alloc实例后终止调用”

为什么要调用析构函数

为什么不调用析构函数?

为什么在“返回 0”后调用析构函数?

在字符串函数中抛出“ char const *”的实例后调用终止

当捕获到异常_should_时,调试“抛出...的实例后调用终止”

C++ 抛出错误“在抛出‘std::bad_alloc’的实例后终止调用”

抛出“ char const *”实例后,调用C ++中的异常处理终止

在C ++中抛出'std :: out_of_range'实例后调用终止

抛出“std::system_error”线程池实例后调用终止

C ++错误:抛出'std :: bad_alloc'实例后终止调用

Tensorflow MNIST:抛出'std :: bad_alloc'实例后调用终止

抛出“std::invalid_argument”实例后调用终止 what(): leetcode 问题中的 stoi 错误

在抛出'std::runtime_error' what() 实例后调用终止:filebuf 和 ostream 的 I/O 错误

抛出'std :: out_of_range'what():vector :: _ M_range_check实例后终止调用

在抛出“std::regex_error”what() 实例后调用终止:括号未关闭

在抛出 'std::out_of_range' what() 实例后调用终止:basic_string::at: __n 错误

std::stoi() 错误——“在抛出 'std::invalid_argument' 实例后终止调用”

抛出'std::bad_alloc'的实例后调用C++终止

c++ 在抛出'std::out_of_range' std::vector 的实例后调用终止

TOP 榜单

热门标签

归档