【问题标题】:Exception message empty异常消息为空
【发布时间】:2017-09-06 14:37:18
【问题描述】:

我有一个异常类如下:

class FileNotFoundException : public std::exception
{
public:
    FileNotFoundException(const char* message) : errorMessage(message){ }
    const char* what() const throw() override
    {
        return this->errorMessage;
    }
private:
    const char* errorMessage;
};

而我throw这个异常是这样的:

std::string message = "Message";
throw ::FileNotFoundException(message.c_str());

但是当我尝试使用以下方法处理它时:

try
{
    // the code that throws 
}
catch(::FileNotFoundException& ex)
{
    std::string message = ex.what(); 
}

string 为空。 如果有人可以提供帮助,我将不胜感激。

【问题讨论】:

  • 未定义的行为。当您 throw 时,您的 std::string message 超出范围并被销毁,您的 errorMessage 指向已释放的内存。
  • 谢谢,你说的都对。愚蠢的是我没有想到这么简单的事情。

标签: c++ exception stdstring


【解决方案1】:

你的问题在这里:

throw ::FileNotFoundException(message.c_str());

您在异常中存储了指向 message 拥有的内存的指针。当message 超出范围(在抛出期间发生)时,数据将不再有效。这意味着this->errorMessage 返回一些未定义的内存。要修复它,您可以将一些真正恒定的字符串传递给您的异常,或者您需要异常来拥有该字符串,例如通过将errorMessage 设为std::string

【讨论】:

    【解决方案2】:

    您不能只存储指向消息的指针。尝试将其存储在std::string 中,或者更好地将其传递给父构造函数。在这种情况下,也许从std::runtime_error 继承会更好。

    这是一个完整的例子:

    #include <iostream>
    #include <string>
    #include <stdexcept>
    
    class FileNotFoundException : public std::runtime_error
    {
    public:
      FileNotFoundException(const char* message) : std::runtime_error(message)
      {
      }
    };
    
    int main()
    {
      try {
        throw ::FileNotFoundException("oops, something happened");
      }
      catch(const ::FileNotFoundException& ex) {
        std::cout << "Exception: '" << ex.what() << "'" << std::endl;
      }
    }
    

    编译运行:

    $ g++ -W -Wall --std=gnu++11 a.cpp -oa
    $ ./a
    Exception: 'oops, something happened'
    

    简而言之(没有细节):std::exception 类没有任何构造函数。它只是所有其他异常使用的父类。另一方面,std::runtime_error 有一个构造函数,可以为您正确存储消息。完整的解释可以在Difference: std::runtime_error vs std::exception()

    我认为这种方法比定义what() 并使用std::string 自己存储消息要好。也就是说,如果您对异常类没有特殊需求。

    您还应该查看C++ exception hierarchy

    【讨论】:

    • 谢谢,这就是解决方案。也谢谢你的例子,我会尽快接受这个作为答案。
    • 您的示例仅修复了它,因为您将一个真正的常量字符串传递给异常构造函数,而不是在传递指针的堆栈上有一个单独的 std::string 变量。将该指针传递给父构造函数并不能修复该行为,字符串仍然不会被复制。
    • @Matthias247 我不知道标准是怎么说的,但 GCC 肯定会复制字符串(我已经仔细检查过)。值得研究。
    • 似乎是真的 - 我现在在这里检查:en.cppreference.com/w/cpp/error/runtime_error std::runtime_error 似乎复制了构造函数参数。没想到会这样。
    • 可能是大多数标准库作者无论如何都只是复制它,因为否则它会非常危险(你正在跳跃堆栈)。所以这可能是每个人都这样做的情况,但也可能是标准没有明确要求它。
    猜你喜欢
    • 2012-01-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-23
    • 2017-04-29
    • 2018-10-29
    • 2021-03-19
    相关资源
    最近更新 更多