STD异常会导致不安全的使用
std exceptions inviting unsafe usage?
建议您总是抛出从std::exception
派生的东西,并且有一些预定义的专门化,例如std::runtime_error
std::exception
的接口以非抛出访问器的形式给出。太好了。现在看看std::runtime_error
class runtime_error : public exception {
public:
explicit runtime_error (const string &);
};
如果我输入这个
try {
foo ();
}
catch (...) {
throw std :: runtime_error ("bang");
}
完全有可能foo
抛出,因为它内存不足,在这种情况下,构造runtime_error
的string
参数也可以抛出。这将是一个throw表达式,它本身也会抛出:这会不会调用std::terminate
?
这是否意味着我们应该这样做:
namespace {
const std :: string BANG ("bang");
}
...
try {
foo ();
}
catch (...) {
throw std :: runtime_error (BANG);
}
但是等等,这个也不行,不是吗?因为runtime_error
要复制它的参数,这也可能抛出…
…因此,这是否意味着没有安全的方法来使用std::exception
的标准专门化,并且您应该始终滚动自己的字符串类,其构造函数只有失败而不抛出?
还是我漏掉了什么诀窍?
我认为你的主要问题是你正在做catch(...)
并翻译为std::runtime_error
,从而失去了原始异常的所有类型信息。你应该用throw()
重新抛出。
实际上,如果你内存不足,你可能会在某个时候抛出bad_alloc
异常,并且没有很多其他你可以或应该做的。如果希望因为分配失败以外的原因抛出异常,那么用有意义的上下文信息构造一个合理的异常对象可能不会有问题。如果在格式化异常对象时遇到内存问题,除了传播内存错误之外,您可以做的事情不多。
你是对的,如果你构造一个新的字符串对象来构造一个异常,这是一个潜在的问题,但如果你想用上下文格式化消息,这通常是无法避免的。请注意,标准异常对象都有一个const char*
构造函数(截至上周),所以如果你想使用一个const char*
,你不必构造一个新的std::string
对象。
std::runtime_error
必须复制它的实参,但不一定是一个新的字符串对象。可能有一个静态分配的内存区域,它可以将参数的内容放入其中。它只需要满足what()
要求,这只需要返回const char *
,它不需要存储std::string
对象。
这将是一个throw表达式,它本身也会抛出:won't This将调用std::terminate?
不,它不会。它只会抛出关于内存不足的异常。控件将无法到达外部throw
部分。
带有抛出复制构造函数的异常类和抛出析构函数一样邪恶。对此我们无能为力。但是等等,这个也不行,不是吗?因为runtime_error是要复制它的参数,这也可能抛出…
std::runtime_error
被设计用来处理常见的运行时错误,而不是内存不足或其他类似的关键异常。基类std::exception
does not做任何可能抛出的事情;也没有std::bad_alloc
。显然,将std::bad_alloc
重新映射为需要动态分配才能工作的异常是一个坏主意。
第一件事是,如果你碰巧有一个bad_alloc异常,你想做什么,因为你的内存不足?
我想说,在一个经典的c++程序中,你会希望程序以某种方式告诉你发生了什么,然后终止。
在一个经典的c++程序中,你会让bad_alloc异常传播到程序的主部分。main函数将包含如下的try/catch语句:int main()
{
try
{
// your program starts
}
catch( const std::exception & e )
{
std::cerr << "huho something happened" << e.what() << std::endl;
}
catch( ... )
{
std::cerr << "huho..err..what?" << std::endl;
}
}
只使用catch(…)在main内部和线程的起始函数处。与Java等其他语言相反,您不需要在本地捕获所有可能的异常。你只要让它们繁殖,直到你在你想要的地方抓住它们。
现在,如果你的代码必须检查std::bad_alloc,你应该只检查catch(const std::bad_alloc &)本地。在这种情况下,它应该做一些其他的事情,而不是仅仅抛出另一个异常。
我在c++编程语言§14.10中也发现c++异常处理机制为自己保留了一点内存来保存异常,这样抛出标准库异常就不会自己抛出异常。当然,如果您确实编写了一些错误的代码,也有可能让异常处理机制耗尽内存。
所以,总而言之,如果你什么都不做,让像bad_alloc这样的大异常在你想要捕获它们的地方很好地传播,在我看来你是安全的。你不应该使用catch(…)或catch(const std::exception &)在除了main函数和线程的起始函数之外的任何地方。捕获所有异常以重新抛出单个异常实际上是最后要做的事情。你失去了c++异常处理机制的所有优势。
- C++/CLI 和 C#/VB 与不安全和外部有什么区别?
- 问:Apache Arrow 数组生成器不安全追加
- 不安全的 MPI 非阻塞通信示例?
- 有没有一种简单的方法来检查C++中的不安全表达式
- 为什么静态向下转换unique_ptr不安全?
- 哪些整数操作不安全
- 升级到G++4.8-exception_ptr.h不支持异常传播
- 为什么这个递归 lambda 函数不安全?
- 解决方法:QPixmap:在GUI线程之外使用pixmap是不安全的
- <regex> std::regex 等同于 Qt 的 QRegularExpression::isValid() 而不触发异常
- 正在匹配不安全的正则线程
- 如何修复编译错误"此函数或变量可能不安全"(strcpy)
- 编译器在 const ref 类型参数上使用临时对象时是否应该警告不安全的行为?
- 实现没有不安全服务器凭据的自定义 AuthMetadataProcessor
- 什么时候关闭__strict_ansi__标志是不安全的
- 原子对象在普通对象安全的任何上下文中都是不安全的
- OpenSSL:将不安全的BIO提升为安全
- 这是对支撑初始器列表的不安全使用情况
- std::vector 不是异常安全的
- STD异常会导致不安全的使用