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
相关产品推荐
相关产品推荐

