std异常邀请不安全的用法?

时间:2011-09-06 11:06:55

标签: c++ exception exception-handling constructor std

建议您始终抛出从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投掷,因为内存不足,在这种情况下构建string runtime_error参数也可以抛出。这将是一个throw-expression,它本身也会抛出:这不会调用std::terminate吗?

这是不是意味着我们应该总是这样做:

namespace {
    const std :: string BANG ("bang");
}

...

try {
    foo ();
}
catch (...) {
    throw std :: runtime_error (BANG);
}

但等待这也行不通,是吗?因为runtime_error将复制其参数,这也可能会抛出......

...所以这不意味着没有安全的方法来使用std::exception的标准特化,并且你应该总是滚动你自己的字符串类,其构造函数只会失败而不抛出?

或者是否有一些我不知道的技巧?

4 个答案:

答案 0 :(得分:5)

我认为您的主要问题是您正在执行catch(...)并转换为std::runtime_error,从而丢失原始异常中的所有类型信息。你应该重新使用throw()

实际上,如果你的记忆力不足,你可能会在某个时刻抛出bad_alloc例外,而且你可以 - 或者应该 - 做的事情并不多。如果希望因为分配失败以外的原因而抛出异常,那么构建具有有意义的上下文信息的合理异常对象可能不会有问题。如果在格式化异常对象时遇到内存问题,那么除了传播内存错误之外,你可以做很多事情。

如果你构造一个新的字符串对象来构造一个异常,那么你是对的,但是如果你想用上下文格式化一个消息,这通常是无法避免的。请注意,标准异常对象都有一个const char*构造函数(截至上周),因此如果您想要使用const char*,则不必构造新的std::string对象

std::runtime_error必须复制它的参数,但不一定是新的字符串对象。可能存在一个静态分配的内存区域,它可以将其参数的内容包含在内。它只需满足what()要求,只需要返回const char *,它不必存储std::string对象。

答案 1 :(得分:3)

  

这将是一个throw-expression,它本身也会抛出:不会这样   会调用std :: terminate?

不,它不会。它会抛出关于内存不足的异常。控件不会到达throw外部。

  

但等待这也行不通,是吗?因为runtime_error是   要复制它的论点,也可能会抛出......

使用抛出复制构造函数的异常类与抛出析构函数一样邪恶。没有什么可以真正做到的。

答案 2 :(得分:3)

std::runtime_error旨在处理通常的运行时错误,而不是 内存不足或其他此类严重异常。基类 std::exception 做任何可能抛出的事情;也没有 std::bad_alloc。显然,将std::bad_alloc重新映射到 要求动态分配工作的异常是个坏主意。

答案 3 :(得分:1)

首先,如果你碰巧遇到 bad_alloc 异常,你会想做什么,因为你内存不足?

我会说在一个经典的C ++程序中,你想让程序试图告诉你发生了什么然后终止。

在经典的C ++程序中,您可以将 bad_alloc 异常传播到程序的主要部分。主要包含像这样的尝试/捕获:

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(...)。与Java等其他语言相反,您不希望在本地捕获所有可能的异常。你只要让它们传播,直到你抓住你想要的地方。

现在,如果您的代码必须特别检查 std :: bad_alloc ,那么您应该只在本地捕获(const std :: bad_alloc&amp;)。在那里做其他事情应该是明智的,而不仅仅是重新抛出另一个例外。

我在C ++编程语言§14.10中也发现,C ++异常处理机制为自己保留了一些内存以保存异常,因此抛出标准库异常本身不会引发异常。当然,如果您真的编写了变态代码,也可以让异常处理机制耗尽内存。

所以,总而言之,如果你什么都不做,并且让像 bad_alloc 这样的大型例外很好地传播到你想捕捉它们的地方,我认为你是安全的。除了主函数和线程的起始函数之外,你不应该在任何地方使用 catch(...) catch(const std :: exception&amp;)

捕获所有异常以重新抛出单个异常实际上是最后一件事。您将失去使用C ++异常处理机制获得的所有优势。