在 std::exception.what() 中使用 std::stringstream

Using std::stringstream in std::exception.what()

想要实现 std::exception class 尤其是 what 功能,我遇到了 std::stringstream 的问题。

简而言之,当我使用这个实现时它打印了一个空字符串:

const char *what() const throw () {
   std::stringstream ss;

   ss << "Error";
   if (_line > -1)
      ss << " at line " << std::to_string(_line);

   ss << ": " << _msg;

   return ss.str().c_str();
}

并在我使用此实现时打印一些内容(例如:"Error at line 3"):

const char *what() const throw () {
   std::stringstream ss;

   ss << "Error";
   if (_line > -1)
      ss << " at line " << std::to_string(_line);

   //ss << ": " << _msg; // COMMENT THIS LINE

   return ss.str().c_str();
}

然后我什至尝试查看它是来自“:”还是来自变量 _msg (std::string),但其中 none 似乎有影响。

这是字符串流方面的缓冲区问题还是我方面的操作问题?如果是操作问题,请告诉我如何使用它。

str 函数 return 是一个字符串值。当您按原样使用它时,该值将是临时的,一旦表达式 (ss.str().c_str()) 结束,字符串对象将立即被破坏。

临时对象被析构意味着你return的指针会立即失效

一种可能的解决方案是将字符串存储在异常对象中,并希望在异常被销毁后指针不会被使用。

ss.str().c_str();

str() 这样做:

Returns a copy of the underlying string [..]

(Source)

所以你现在有一个临时的 std::string,你可以在上面调用 c_str()。此 return 是指向 (C) 字符串的指针 由该临时 std::string 管理

因此,一旦该语句结束(在本例中为函数 returns),临时 std::string 将被破坏,这将删除/释放您从中获得的指针所在的内存c_str() 指向。

所以你有一个无效的指针,访问它是未定义的行为。

解决方案:

  1. 复制 C 字符串,return。 不好 因为现在调用者必须释放这个副本,而这不是 std::exception::what()

    的“API”的一部分
  2. std::string 成员添加到您的 class,用 return 值 [=12] 填充 that =](最好在 class 的构造函数中...)和 return 成员内部 C 字符串:

    struct my_exception : public std::exception {
      std::string description;
      my_exception() {
        std::stringstream ss;
        // code to fill ss
        description = ss.str();
      }
      char const * what() const override {
        return description.c_str();
      }
    };
    

保留核桃的重要评论:

You need to be careful with this though. std::string may throw when copied and if that happens e.g. with an exception handler catching by copy, std::terminate will be called and the program will abort. On the other hand it will probably only throw when memory allocation fails which might be an error that you consider unrecoverable anyway (or not).