有些文章总结“永远不会从析构函数中抛出异常”,而“std :: uncaught_exception()没有用”,例如:
但似乎我没有明白这一点。所以我写了一个小测试示例(见下文)。
由于测试示例一切正常,我会非常感谢有关它可能出错的一些评论吗?
测试结果:
./主
Foo::~Foo(): caught exception - but have pending exception - ignoring int main(int, char**): caught exception: from int Foo::bar(int)
./ main 1
Foo::~Foo(): caught exception - but *no* exception is pending - rethrowing int main(int, char**): caught exception: from Foo::~Foo()
示例:
// file main.cpp
// build with e.g. "make main"
// tested successfully on Ubuntu-Karmic with g++ v4.4.1
#include <iostream>
class Foo {
public:
int bar(int i) {
if (0 == i)
throw(std::string("from ") + __PRETTY_FUNCTION__);
else
return i+1;
}
~Foo() {
bool exc_pending=std::uncaught_exception();
try {
bar(0);
} catch (const std::string &e) {
// ensure that no new exception has been created in the meantime
if (std::uncaught_exception()) exc_pending = true;
if (exc_pending) {
std::cerr << __PRETTY_FUNCTION__
<< ": caught exception - but have pending exception - ignoring"
<< std::endl;
} else {
std::cerr << __PRETTY_FUNCTION__
<< ": caught exception - but *no* exception is pending - rethrowing"
<< std::endl;
throw(std::string("from ") + __PRETTY_FUNCTION__);
}
}
}
};
int main(int argc, char** argv) {
try {
Foo f;
// will throw an exception in Foo::bar() if no arguments given. Otherwise
// an exception from Foo::~Foo() is thrown.
f.bar(argc-1);
} catch (const std::string &e) {
std::cerr << __PRETTY_FUNCTION__ << ": caught exception: " << e << std::endl;
}
return 0;
}
ADDED :换句话说:尽管有些文章中的警告可以按预期运行 - 那么它可能出现什么问题?
答案 0 :(得分:8)
Herb Sutter指的是一个不同的问题。他在谈论:
try
{
}
catch (...)
{
try
{
// here, std::uncaught_exception() will return true
// but it is still safe to throw an exception because
// we have opened a new try block
}
catch (...)
{
}
}
所以问题是如果std::uncaught_exception()
返回true,你不确定是否可以安全地抛出异常。 1}}为了安全而在std::uncaught_exception()
返回true时最终必须避免抛出异常。
答案 1 :(得分:6)
您的代码在技术上没有任何问题。这是非常安全的,因为你不会意外地终止,因为你在不安全的情况下抛出异常。问题是它也没有用,因为它有时也不会在安全的情况下抛出异常。您的析构函数的文档基本上必须说“这可能会或可能不会抛出异常。”
如果它偶尔不会抛出异常,你也可能永远不会抛出异常。这样,你至少是一致的。
答案 2 :(得分:0)
Herb Sutter正在讨论当类T
的对象中存在未捕获的异常时,类U
的对象被销毁的情况。 std::uncaught_exception()
会在true
析构函数中返回T
。析构函数将无法确定在堆栈展开期间是否调用它。如果是的话,它一定不能抛出,否则就像往常一样。
类U
在析构函数中使用类T
时会出现问题。 U
会发现自己处理的是一个无用的T
对象,它拒绝在析构函数中做任何冒险的事情(可能包括编写日志文件或将事务提交到数据库)。
Herb Sutter建议永远不要扔掉析构函数,这是一个好主意。但是,C ++ 17提供了另一种选择。它引入了std::uncaught_exceptions()
,可用于确定析构函数是否可以抛出。以下示例显示了在C ++ 14模式下是否符合的问题。如果在C ++ 17模式下编译,它将正常工作。
#include <exception>
#include <iostream>
#include <string>
class T
{
public:
~T() noexcept(false)
{
#if __cplusplus >= 201703L
// C++17 - correct check
if (std::uncaught_exceptions() == uncaught_exceptions_)
#else
// Older C++ - incorrect check
if (!std::uncaught_exception())
#endif
{
throw (std::string{__PRETTY_FUNCTION__} + " doing real work");
}
else
{
std::cerr << __PRETTY_FUNCTION__ << " cowardly quitting\n";
}
}
private:
#if __cplusplus >= 201703L
const int uncaught_exceptions_ {std::uncaught_exceptions()};
#endif
};
class U
{
public:
~U()
{
try
{
T t;
}
catch (const std::string &e)
{
std::cerr << __PRETTY_FUNCTION__ << " caught: " << e << '\n';
}
}
};
int main()
{
try
{
U u;
throw (std::string{__PRETTY_FUNCTION__} + " threw an exception");
}
catch (const std::string &e)
{
std::cerr << __PRETTY_FUNCTION__ << " caught: " << e << '\n';
}
return 0;
}