如何构造< stdexcept>或< system_error>异常不抛出? [英] How to construct a <stdexcept> or <system_error> exception without throwing?

查看:303
本文介绍了如何构造< stdexcept>或< system_error>异常不抛出?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

< 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(hasError)
throw std :: runtime_error(BOO!); //可能会抛出std :: bad_alloc?

,但是在其他地方预先构建异常似乎更加安全,例如: / p>

  struct A {
//在堆分配新A()期间,我们可以期望std :: bad_alloc:
A():m_someException(BOO!){}
void f(){
/ * Do stuff * /
if(hasError)
throw m_someException;
/ *请注意,根据§18.8.1.2,从`std :: exception`派生的所有标准库
类必须公开
可访问的复制构造函数和复制赋值运算符
没有出现异常。在实现中,这样的
异常实例很可能共享公共字符串
及其所有副本。 * /
}
std :: runtime_error常量m_someException;
};

这使我非常谨慎的库引发任何这样的异常,例如甚至 C ++ 11中的< regex> 中的



strong>为什么这些异常没有no-throw / noexcept构造函数?



PS:我个人会留下

$

$

 

$

 

code> if(hasError)
throw std :: runtime_error(BOO!); //可能会抛出std :: bad_alloc?到达此 throw 时,

应该已经照顾所有清理所以在大多数情况下,有一个 std :: bad_alloc 抛出而不是 std :: runtime_error 不会有太大的区别。



唯一的例外情况是,当异常用于控制程序的流程时 - 我倾向于经常使用以下代码: / p>

  try {
auto face = detectFace(); //可以抛出一个自定义的no_face_exception或一个many_faces_exception
//使用face
}
catch(no_face_exception& e){
std :: cout< no face detected\\\
;
}
catch(many_faces_exception& e){
std :: cout< too much faces detected\\\
;
}

在这种特殊情况下,分配内存失败会导致 detectFace 抛出 std :: bad_alloc ,这将导致灾难性的崩溃。首先在抛出之前分配异常,如你所说,根本不会改变任何东西 - 程序仍然会与一个 std :: bad_alloc 崩溃,因为分配仍然会失败。关于这个问题的唯一方法是简单地捕获 std :: bad_alloc

  catch(std :: bad_alloc& e){
std :: cout< bad alloc reported\\\
;
}


Exceptions defined in <stdexcept> (e.g. std::logic_error, std::runtime_error and their subclasses such as std::system_error) have constructors expecting string arguments, e.g.:

domain_error(const string& what_arg);
domain_error(const char* what_arg);

with postconditions

strcmp(what(), what_arg.c_str()) == 0
strcmp(what(), what_arg) == 0

respectively. There is no requirement that these arguments passed to the constructors remain valid during the lifetime of these exceptions, so the only way to ensure that the postconditions hold, is to duplicate and store these dynamic strings. This requires memory, so I assume that their construction itself may throw std::bad_alloc or similar. This causes problems, because every code example I've seen in the wild encourages people to write code like

if (haveError)
    throw std::runtime_error("BOO!"); // May throw std::bad_alloc instead?!

whereas it would seem to be much safer to construct the exception beforehand in some other place, e.g:

struct A {
    // During heap allocation new A() one would 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;
};

This makes me very cautious of libraries which throw any such exceptions, e.g even regex_error from <regex> in C++11!!!

Why don't these exceptions have no-throw/noexcept constructors?

PS: Personally I would have left what() a pure abstract method at this point in the exception ancestry chain.

解决方案

if (haveError)
    throw std::runtime_error("BOO!"); // May throw std::bad_alloc instead?!

by the time you reach this throw you should have already taken care of all clean up so in most cases, having a std::bad_alloc thrown instead of a std::runtime_error won't make much of a difference really.

The only exceptional case, I can thing of is when exceptions are used to control the flow of a program - I tend to do this very often with code such as:

try { 
  auto face = detectFace(); // may throw a custom no_face_exception or a many_faces_exception
  // do work with face
} 
catch (no_face_exception& e) {
  std::cout << "no face detected\n";
}
catch (many_faces_exception& e) {
  std::cout << "too many faces detected\n";
}

In this particular case failure to allocate memory would cause detectFace to throw a std::bad_alloc instead which would cause a disastrous crash. First allocating the exceptions before throwing, as you suggested, would not change anything at all - the program would still crash with a std::bad_alloc since the allocation would still fail. The only way around this problem would be to simply catch the std::bad_alloc:

catch (std::bad_alloc& e) {
  std::cout << "bad alloc reported\n";
}

这篇关于如何构造&lt; stdexcept&gt;或&lt; system_error&gt;异常不抛出?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆