【问题标题】:std::exception using message from local object使用来自本地对象的消息的 std::exception
【发布时间】:2013-05-21 19:16:46
【问题描述】:

以下代码是否安全地抛出带有自定义消息的异常?

#include <exception>
#include <sstream>
#include <string>
#include <iostream>

int main() {
  try {
    std::ostringstream msg;
    msg << "give me " << 5;
    throw std::exception(msg.str().c_str());
  } catch (std::exception& e) {
    std::cout << "exception: " << e.what();
  }
}

使用 VC++-2008 这给出了:

exception: give me 5

但是现在我想知道为什么来自本地对象msg 的消息“给我5”仍然在catch 块中可用?在打印消息时,流对象和临时字符串对象都应该被删除吗?顺便说一句:这种为异常生成消息的方式似乎也适用于多个函数,并且如果在打印异常之前在 catch 块中分配了新内存。

或者是否有必要使用 std::string 成员定义自定义异常类,以便在打印之前安全地保留消息。

【问题讨论】:

    标签: c++ visual-c++ exception


    【解决方案1】:

    绝对安全。将 C 字符串作为单个参数的构造函数会复制该字符串。采用 C 字符串和长度参数的构造函数允许您指定不分配内存并存储指向字符串的指针(长度参数被忽略)。

    请注意,这两个构造函数是std::exception 类的扩展,不是标准的。另请注意,将 C 字符串作为单个参数的构造函数未标记为显式。

    【讨论】:

    • 您可能还想阅读关于异常的 boost 文章:boost.org/community/error_handling.html,他们给出的建议特别是第 3 点和第 4 点:不要嵌入 std::string 对象,格式化 what()消息点播
    • 很好的建议,但我看不出第 3 点和第 4 点对这个问题有什么特别之处。
    • @CaptainObvlious 逐字逐句地回答我的问题,提升链接似乎离题了。但是我遇到了这个问题/问题,因为我不知道那里指出的考虑因素。当遵循上述第 3 点和第 4 点时,我的问题就变得多余了。这对我来说是一个非常有价值的提示。到目前为止,我一直避免定义自己的异常类(支持 VC 的 std::exception)。感谢您的回答,我确信我没有直接的问题。感谢 boost 链接,我将从现在开始考虑使用自定义异常类来避免“过早格式化”。
    • 是的,我想我太从字面上理解了,把头发分开了;)我很抱歉。
    【解决方案2】:

    没关系。

    根据 §15.1/3:

    抛出异常复制初始化(8.5,12.8)一个临时对象, 调用异常对象。

    和§15.1/4:

    异常对象的内存以未指定的方式分配 方式,除非在 3.7.4.1 中注明。如果处理程序通过重新抛出退出, 控制权被传递给同一异常的另一个处理程序。这 异常对象在最后一个剩余活动之后被销毁 异常处理程序以除重新抛出以外的任何方式退出...

    所以throw expression之后:

    表达式将被复制(新对象将由复制构造函数创建),您不必担心本地对象。

     

    关于你担心的msgconst char*...这里是微软的实现:

    exception::exception(const char * const & _What)
    : _Mywhat(NULL), _Mydofree(false)
    {
       _Copy_str(_What);
     //^^^^^^^^^^^^^^^^^
    }
    
    void exception::_Copy_str(const char * _What)
    {
       if (_What != NULL)
       {
          const size_t _Buf_size = strlen(_What) + 1;
          _Mywhat = static_cast<char *>(malloc(_Buf_size));
           if (_Mywhat != NULL)
           {
             _CRT_SECURE_STRCPY(const_cast<char *>(_Mywhat), _Buf_size, _What);
           //^^^^^^^^^^^^^^^^^^
             _Mydofree = true;
           }
       }
     }
    

    它复制_What,而不仅仅是存储指针。

    【讨论】:

    • 我很担心 VC 的 std::exception 版本,它只包含一个字符指针。但似乎底层的char数组是由构造函数复制的。
    • @coproc:更新让您感到舒适。
    【解决方案3】:

    不,这不安全,因为std::exception 在标准中没有采用 char* 的构造函数。您正在使用 MS 扩展。为了使其便携和安全,请改用 std::runtime_error,您可以在 ctor 中将 std::string 传递给它。

    【讨论】:

    • 感谢您指出std::runtime_error,从现在开始我将使用它。我想知道为什么到目前为止我没有遇到任何麻烦,VC 的std::exception 只持有一个字符指针(而不是std::string)。但似乎 VC 的 std::exception 正在创建自己的 char 数组副本。
    • 这是安全的,请查看此问题的其他答案。作为一个扩展并不会使使用非便携式不安全。
    • 不可移植是不安全的。移动到不同的版本或不同的编译器可能会导致此代码编译但在运行时调用未定义的行为。既然有几乎相同的便携式替代品,为什么要冒险?
    猜你喜欢
    • 1970-01-01
    • 2012-04-23
    • 1970-01-01
    • 1970-01-01
    • 2011-02-03
    • 1970-01-01
    • 1970-01-01
    • 2013-06-26
    • 1970-01-01
    相关资源
    最近更新 更多