c++ - std::exception 是否拥有 `what()` ?

标签 c++ exception c++14 std std-system-error

我从 std::system_error 派生我自己的异常,将其称为 MyException 并覆盖了 what() 来计算和回我的消息。 MyException 的初始化列表不会调用接收消息的 system_error 构造函数覆盖。

如果我捕获一个 MyException 并将其复制到一个 std::exception 调用 上的 what() 的结果std::exceptionnullptr。这是有道理的。

我的问题是,如果我确实使用了在初始化 MyException 时获取消息的 system_exception 构造函数,是否指定 system_error 将获取消息的拷贝并拥有它并释放它?

我假设这将使 MyExceptionstd::exception 拷贝能够返回有效的 what()。尽管每次创建新的 MyExceptions 时都需要计算“什么”,但我会对性能造成影响;我不能仅在第一次调用 what() 时懒惰地计算它。

我有点担心 'what' 字符串的所有权,因为 what() 返回 char* 而不是 const std::string&

代码是这样的(我还没有编译这个):

    class MyException : public std::system_error
    {
        std::string what_;
    public:
        MyException(int errorValue, const std::error_category& category)
            : std::system_error(errorValue, category)
        {}

        char* what() const
        {
           what_ = "MyException: " + to_string(code().value());
           return what_.c_str();
        }
    };

    int main()
    {
        std::exception ex;

        try
        {
            throw MyException(4, system_category());
        }
        catch( const MyException& e )
        {
            ex = e;
        }

        printf("what= %s", ex.what());

        return 1;
    }

最佳答案

My question is, if I do use the constructor of system_exception that takes a message when initializing MyException, is it specified that system_error will take a copy of the message and own it and free it?

是的,这是由标准保证的。

开始,std::exception不拥有whatstd::runtime_error做。 std::runtime_error的构造函数是这样定义的([runtime.error]p2-5):

runtime_error(const string& what_arg);

Effects: Constructs an object of class runtime_error.
Postcondition: strcmp(what(), what_arg.c_str()) == 0.

runtime_error(const char* what_arg);

Effects: Constructs an object of class runtime_error.
Postcondition: strcmp(what(), what_arg) == 0.

因此,它必须存储 what_arg拷贝在内部,因为对传入值的生命周期没有要求。

接下来是[异常]p2:

Each standard library class T that derives from class exception shall have a publicly accessible copy constructor and a publicly accessible copy assignment operator that do not exit with an exception. These member functions shall meet the following postcondition: If two objects lhs and rhs both have dynamic type T and lhs is a copy of rhs, then strcmp(lhs.what(), rhs.what()) shall equal 0.

所以,必须有一个复制构造函数,它绝不能抛出,并且拷贝必须为what()保持相同的返回值。 .复制赋值运算符也是如此。

将所有这些放在一起,我们可以推测 std::runtime_error必须保留您为 what_arg 传递的值在引用计数的字符串内部(以避免复制时分配的异常),并且无论复制和/或切片如何,该值都将持续存在 - 但仅限于 std::runtime_errorstd::exception ! (有关 what 的存储的基本原理和要求的更多信息可以在 @HowardHinnant 的这个非常有趣的答案中找到:move constructor for std::runtime_error)

std::system_error继承自 std::runtime_error ,因此对于它和从它派生的任何类型都同样适用(只要派生类型保持非抛出复制构造函数不变)。

I'm assuming this would enable a std::exception copy of MyException to be able to return a valid what().

不!当你制作 std::exception MyException拷贝 , 你是 slicing对象的派生类型低于 where what的值是物理存储的。如果您必须复制您的异常,您可以使用的最少派生类型是std::runtime_error。 . (当然,您始终可以安全地将 std::exception reference 设为 MyException。)换句话说,永远 不可能获得有意义的字符串来自std::exception 对象what() .


此代码具有您想要的可移植行为:

#include <cstdio>
#include <stdexcept>
#include <system_error>
#include <string>

class MyException : public std::system_error {
public:
    MyException(int errorValue, std::error_category const& category)
      : std::system_error(
            errorValue, category,
            "MyException: " + std::to_string(errorValue)
        )
    { }
};

int main() {
    std::runtime_error ex;

    try {
        throw MyException(4, system_category());
    } catch(MyException const& e) {
        ex = e;
    }

    std::printf("what= %s", ex.what());
}

我会说编写分配的异常构造函数是一种糟糕的形式(出于显而易见的原因),但考虑到我所知道的每个当前标准库实现都使用 short-string optimization对于 std::basic_string<> ,这在实践中极不可能成为问题。

关于c++ - std::exception 是否拥有 `what()` ?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38479375/

相关文章:

objective-c - ***由于未捕获的异常 'NSInvalidArgumentException'而终止应用程序

c++ - shared_ptr 的对象池模式是否可能?

c++ - 如何在opengl中以给定的起点绘制弧线?

c++ - 异常c++中的堆栈展开

java - Swing 号码格式异常

c++ - 以随机大小初始化 std::array

c++ - gtest : run TEST_P inside 2 TEST_Fs

c++ - 如何处理 C++ 文件以删除 ifdef 输出代码

c++ - 如何在 C++/WinRT 中获取底层类型的 TypeName?

c++ - 将模板参数限制为仅具有不同构造函数签名的一组类