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

如何编写继承自std::invalid_argument的高效C++自定义异常类

自定义异常类实现与异常处理方案

一、原有代码的核心问题

  • 异常类构造函数没有调用父类std::invalid_argument的构造函数,不符合C++继承的规范要求。
  • 你声明了异常类的构造函数需要接收const std::string&入参,但抛出异常时写的是const Foo_Exception foo_exc; 没有传递任何参数,会直接编译报错。
  • 在setCharacter内部抛出异常后立刻捕获,完全违背异常的设计初衷:异常是用来向能处理错误的上层调用者传递错误信息的,当前层无法处理的错误才需要抛出,当场捕获的写法和直接打印错误信息后返回没有区别,平白增加了异常的开销。
  • 自行维护std::string成员重写what()的逻辑是冗余的,标准库的父类已经实现了对应的能力。

二、符合要求的高效异常类实现

你要求仅执行1次堆分配,提前计算拼接内容总长度做reserve即可实现,完整实现代码如下:

#include <stdexcept>
#include <string>
#include <unordered_set>
#include <iostream>
#include <cstdlib>

// 全局静态允许字符集合,可根据需求修改
inline const std::unordered_set<char> allowed_chars = {'a', 'b', 'c' /* 其他允许的字符 */};
// 你需要的constexpr常量
constexpr int ERROR_CODE = 40010;

class Foo_Exception : public std::invalid_argument
{
public:
    explicit Foo_Exception(char invalid_char) 
        : std::invalid_argument(build_error_msg(invalid_char))
    {}

private:
    // 消息拼接逻辑放在静态私有函数里,构造初始化列表直接调用
    static std::string build_error_msg(char invalid_char)
    {
        // 按你的要求预留130000字节空间,仅执行1次堆分配
        constexpr size_t RESERVE_SIZE = 130000;
        std::string msg;
        msg.reserve(RESERVE_SIZE);

        msg = "Invalid character passed, error code: ";
        msg += std::to_string(ERROR_CODE);
        msg += ", invalid char: '";
        msg += invalid_char;
        msg += "', only characters in allowed set are accepted.";
        // 其他拼接内容可直接在这里追加,已预留足够空间不会再触发堆分配
        return msg;
    }
};

实现说明

  • 该类继承std::invalid_argument完全符合你的需求,std::invalid_argument本身就是用来标识调用者传入非法参数的错误,这类错误确实可通过修正上层调用的传参逻辑避免。
  • 不需要自行维护std::string成员和重写what(),父类std::invalid_argument的构造函数会自动存储传入的错误字符串,父类自带的what()方法会直接返回该字符串的C风格指针,效果和你自行实现的逻辑完全一致,还减少了冗余代码。
  • 消息拼接逻辑放到静态私有函数中,在构造函数的初始化列表直接调用,符合C++标准异常类的构造规范。
  • 提前reserve足够的内存空间,保证整个拼接过程仅执行1次堆分配,满足你的性能要求。
  • 构造函数直接接收非法字符作为入参,使用更简洁,不需要上层调用者自行拼接错误字符串。

三、异常处理位置的选择

绝对不要把try-catch放在setCharacter内部,理由如下:

  • 异常的核心作用是把错误信息传递给能处理这个错误的上层,你在setCharacter里当场抛出当场捕获,相当于用异常实现了普通的错误打印逻辑,完全浪费了异常的能力,而且性能比直接打印还差。
  • 上层调用者可能有不同的错误处理逻辑:比如有的场景需要打印日志,有的场景需要弹出提示框,有的场景需要回滚操作,你把catch写在setCharacter里相当于写死了处理逻辑,上层无法自定义。
  • 正确的做法是把try-catch放到main函数里,或者你上层的业务逻辑入口处,示例代码如下:
void setCharacter(const char& c)
{
    if (allowed_chars.find(c) == allowed_chars.end())
    {
        // 检测到非法参数直接抛异常,不需要在当前层处理
        throw Foo_Exception(c);
    }

    // 剩下的正常逻辑
    std::cout << "Set character: " << c << std::endl;
}

int main()
{
    try
    {
        setCharacter('-'); // 非法字符,会抛异常
    }
    catch(const Foo_Exception& e)
    {
        // 上层统一处理异常:比如打印日志、返回错误码等
        std::cerr << "Error: " << e.what() << std::endl;
        return EXIT_FAILURE;
    }

    return EXIT_SUCCESS;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.26 19:15:04