【发布时间】:2014-12-07 12:12:44
【问题描述】:
我正在尝试编写一个单元测试来检测我班级的 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");
}
现在,delete 调用 std::terminate(),即使在另一个异常处于活动状态时未调用 throw。 (即std::uncaught_exception() 是错误的。)而且我显然捕获了所有异常!
是我做错了什么,还是 g++ 被编程为总是在析构函数中这样做?
更新:
dyp 在下面的 cmets 中的答案有效!以下不直接调用std::terminate():
~A() noexcept(false) { throw ...; }
还可以作为参考,了解您为什么不希望使用析构函数,此页面非常好;
为了澄清,有完整版的析构函数。正如我们所看到的,我首先发布了一条消息(它通常会出现在您的控制台中,也可能会出现在日志中)。其次,我确保我们尚未管理异常。最后,我抛出了一个名为 exception_exit 的异常,它预计会强制 terminate() 尽管在 GUI 应用程序中您可能希望显示某种 MessageBox 让用户知道发生了什么事(因为您可以捕获消息,然后将其显示给用户)然后强制关闭应用程序。
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++11 起,大部分析构函数默认为
noexcept。尝试将其声明为noexcept(false)(或者更好的是,尝试设计您的类不要放入析构函数)。 -
我很好奇你期望发生什么。假设你捕获了异常——你有一个超出范围但没有被破坏的对象。你是做什么的?
-
@dyp,谢谢你的回答!那行得通。很抱歉,我不能给你分数,因为马特和乔纳森认为这是一个重复的论点,为什么你不应该做我想做的事,但没有回答我的问题。如果你问我,这很烦人。
-
@DavidSchwartz,答案就在我的第一句话中:我正在编写单元测试。仅当测试运行完全相同的代码和最终代码时,安全代码才是安全的。如果您开始添加
#ifdef DEBUG之类的内容,那么您有两个版本的代码,这绝对不安全。 -
@AlexisWilke 你没有真正回答我的问题。你期待发生什么?当异常被捕获时,对象超出范围但没有被破坏,对吧?在这种情况下,您预计会发生什么?
标签: c++ exception g++ destructor throw