【发布时间】:2016-03-19 20:26:29
【问题描述】:
在<stdexcept> 中定义的异常(例如std::logic_error、std::runtime_error 及其子类,例如std::system_error)具有期望字符串参数的构造函数,例如:
domain_error(const string& what_arg);
domain_error(const char* what_arg);
后置条件
strcmp(what(), what_arg.c_str()) == 0
strcmp(what(), what_arg) == 0
分别。不要求传递给构造函数的这些参数在这些异常的生命周期内保持有效,因此确保后置条件成立的唯一方法是复制并存储这些动态字符串。这需要内存,所以我假设它们的构造本身可能会抛出std::bad_alloc 或类似的东西,这通常是最出乎意料的。这会导致问题,因为我在野外看到的每个代码示例都鼓励人们编写类似
if (haveError)
throw std::runtime_error("BOO!"); // May throw std::bad_alloc instead?!
而在其他地方预先构造异常似乎更安全,例如:
struct A {
// During allocation of A one would often expect std::bad_alloc anyway:
A() : m_someException("BOO!") {}
void f() {
/* Do stuff */
if (haveError)
throw m_someException;
/* Note that according to §18.8.1.2 all standard library
classes deriving from `std::exception` must have publicly
accessible copy constructors and copy assignment operators
that do not exit with an exception. In implementations such
exception instances most likely share the common string
with all their copies. */
}
std::runtime_error const m_someException;
};
这让我对抛出任何此类异常的库非常谨慎,例如,即使是 C++11 中 <regex> 中的 regex_error !!!
为什么这些异常没有 no-throw/noexcept 构造函数? C++ 核心指南对此有发言权吗?
PS:我个人会在异常祖先链中的这一点上留下what() 一个纯抽象方法。
EDIT 09.10.2017:这是一个证明 std::runtime_error 构造可以抛出 std::bad_alloc 的 PoC:
#include <cstddef>
#include <cstdlib>
#include <new>
#include <stdexcept>
#include <string>
bool throwOnAllocate = false;
void * operator new(std::size_t size) {
if (!throwOnAllocate)
if (void * const r = std::malloc(size))
return r;
throw std::bad_alloc();
}
void operator delete(void * ptr) { std::free(ptr); }
int main() {
std::string const errorMessage("OH NOEZ! =(");
throwOnAllocate = true;
throw std::runtime_error(errorMessage);
}
【问题讨论】:
-
“而在其他地方预先构造异常似乎更安全”——我不明白。如果您的
throw std::runtime_error("BOO!");由于没有足够的可用内存而引发不同的异常,则预先分配std::runtime_error将仍然要求该内存可用。我看到的唯一变化是bad_alloc会更早被抛出。 -
如果不会抛出如此严重的异常,那就更可怕了。不要为小事操心,如果 bad_alloc 迫在眉睫,那么当你要扔的时候发生它并不重要,它总是会发生,你永远不会对此感到高兴。
-
我猜如果你担心程序在 100% 错误安全条件下运行,你不应该使用 stdexcept,而是你自己的异常,它存储指向 c-strings 的指针,初始化为程序启动或类似的,您可以控制内存影响。
-
我认为你不应该为
使用 runtime_error 而烦恼,毕竟正则表达式可能也会在正常工作期间分配内存。如果您想处理 OOM,则根本不应该使用标准正则表达式,否则您应该准备好处理这些错误。 -
@OP 即使他们有 noexcept ctors——它也不会帮助你。见this。我建议只是从 any throw 语句中期待 std::bad_alloc 。很容易理解为什么一旦你意识到异常和 C 风格的 EH 之间的主要区别——后者在函数签名中提到了错误对象类型,并且调用者在堆栈上分配了所需的内存。除了例外情况,您不能这样做——必须使用某种“动态”分配。
标签: c++ exception out-of-memory