You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++继承std::exception重写what函数如何避免子类重复实现

解决方案

你当前的实现存在两个核心问题:

  • 拼接字符串产生的是临时std::string对象,what函数返回后临时对象会被销毁,返回的指针是野指针,属于未定义行为。
  • 基类默认无法直接获取子类的类型标识,需要通过模板中间层传递该信息,即可避免每个子类重复实现what函数。

最优实现方案(CRTP奇异递归模板模式)

该方案仅需在基类实现一次what,新增异常类仅需少量代码即可完成,维护成本极低。

#include <exception>
#include <string>

class Exception : public std::exception {
protected:
    // 提前拼接好的完整错误信息,生命周期和异常对象绑定
    std::string m_errMsg;
    // 构造函数设为protected,仅允许子类调用,传入子类对应的错误标识
    explicit Exception(const char* errLabel) {
        m_errMsg = "A game related error has occurred: ";
        m_errMsg += errLabel;
    }
public:
    // 统一实现what函数,无需子类重写
    const char* what() const noexcept override {
        return m_errMsg.c_str();
    }
};

// 中间模板层,传递子类类型
template<typename Derived>
class ExceptionCRTP : public Exception {
protected:
    // 手动传入错误标识的构造函数,兼容性最好,输出内容完全可控
    explicit ExceptionCRTP(const char* errLabel) : Exception(errLabel) {}

    // 若接受RTTI且不需要严格控制输出格式,可启用以下构造函数,子类无需手动传类名
    // ExceptionCRTP() : Exception(typeid(Derived).name()) {}
};

子类实现示例,无需重写what函数:

class IllegalArgument : public ExceptionCRTP<IllegalArgument> {
public:
    IllegalArgument() : ExceptionCRTP("IllegalArgument") {}
};

class IllegalCell : public ExceptionCRTP<IllegalCell> {
public:
    IllegalCell() : ExceptionCRTP("IllegalCell") {}
};

效果验证

捕获异常时可自动输出对应类型的错误信息:

#include <iostream>
int main() {
    try {
        throw IllegalArgument();
    } catch (const std::exception& e) {
        // 输出:A game related error has occurred: IllegalArgument
        std::cout << e.what() << std::endl;
    }
    return 0;
}

补充说明

  • 如果选择用typeid自动获取类名,需要注意不同编译器对typeid(T).name()的返回值格式不同,比如GCC会返回带长度前缀的名称如15IllegalArgument,如果需要统一格式建议手动传入类名字符串。
  • 错误信息存储在异常对象的成员变量中,生命周期和异常对象一致,符合std::exception的what函数语义要求,不存在野指针问题。

内容的提问来源于stack exchange,提问作者Saleh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.06 11:51:02