c++ 错误类别

本文深入探讨了C++标准库中的错误处理机制,重点介绍了std::error_category类及其派生类,包括std::generic_category、std::system_category、iostream_category和future_category。通过实例展示了如何自定义错误类别及错误消息,帮助读者理解并掌握C++中的错误处理实践。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

std::error_category

std::error_category是错误类别的基类,实现了并且提供了如下4个常见错误类别,每个特定类别类定义 error_code - error_condition 映射,并保有所有 error_condition 的解释字符串。错误类别类的对象被当做单例,按引用传递
std::generic_category(通用错误类别,它用于鉴别对应 POSIX errno 码的错误条件)
std::system_category(操作系统错误类别,用于操作系统提供的错误码,linux和generic_category基本无差异)
iostream_category(iostream 错误类别,用于鉴别提供于 std::ios_base::failure 类型异常的 error_code)
future_category(future 错误类别,用于鉴别 std::future_error 类型异常中提供的 error_code)

stl提供了如下图所示

std::error_category
std::generic_category
std::system_category
iostream_category
future_category

std::error_category提供的接口

class error_category
{
public:
    error_category();
    virtual ~error_category() noexcept;
    /**
    返回指向指定错误类别名称的空终止字节字符串的指针
    */
    virtual const char *name() const noexcept = 0;
    /**
    返回错误描述
    */
    virtual string message(int _Errval) const = 0;
    /**
    映射 error_code 到 error_condition
    等价于 std::error_condition(code, *this)
    */
    virtual error_condition default_error_condition(int _Errval) const noexcept;
    /**
    检查对于 *this 所表示的 error_category , error_code 是否等价于 error_condition
    等价于 default_error_condition(code) == condition
    */
    virtual bool equivalent(int _Errval, const error_condition& _Cond) const noexcept;
    /**
    等价于 *this == code.category() && code.value() == condition
    */
    virtual bool equivalent(const error_code& _Code, int _Errval) const noexcept;
    /**
    比较两个error_category
    */
    bool operator==(const error_category& _Right) const noexcept;
    bool operator!=(const error_category& _Right) const noexcept;
    bool operator<(const error_category& _Right) const noexcept;
    error_category(const error_category&) = delete;
    error_category& operator=(const error_category&) = delete;
};

示例

#include <iostream> //     iostream_category<ios>
#include <future> //future_category
#include <system_error>  //system_category,generic_category

using namespace std;

class _database_category : public std::error_category
{
public:
    virtual const char *name() const noexcept
    {
        return ("database");
    }
    virtual string message(int _Errval) const
    {
        if (*(database_errc + _Errval))
        {
            return std::string(database_errc[_Errval]);
        }
        return "database-unkown error";
    }
private:
    static const char* database_errc[];
};

_database_category&  database_category()
{
    //return (_Immortalize<_database_category>()); // 库提供的单例
    static  _database_category dc;//直接使用局部静态变量
    return dc;
}

const char*  _database_category::database_errc[] =
{
    "connect db fail",//在实际中为了兼容error_code一般设置0的错误码是无错的情况
    "exesql error",
    NULL
};


int main()
{
    cout << "cate-name-->" << std::system_category().name() << endl  // windows可以使用 errlook.exe 1校验
         << "error 1-->" << std::system_category().message(1).c_str() << endl;

    cout << "cate-name-->" << std::generic_category().name() << endl
        << "error 1-->" << std::generic_category().message(1).c_str() << endl;

    cout << "cate-name-->" << std::iostream_category().name() << endl
        << "error 1-->" << std::iostream_category().message(1).c_str() << endl;

    cout << "cate-name-->" << std::future_category().name() << endl
        << "error 1-->" << std::future_category().message(1).c_str() << endl;
 
    cout << "cate-name-->" << database_category().name() << endl
        << "error 1-->" << database_category().message(1).c_str() << endl;
    cout << "---------------" << endl;

    std::error_condition ed = database_category().default_error_condition(0);

    cout << ed.message().c_str() << endl;
    cout << ed.value() << endl;
    cout <<ed.category().name()<<endl;

    return 0;
}

示例结果

cate-name-->system
error 1-->函数不正确。

cate-name-->generic
error 1-->operation not permitted
cate-name-->iostream
error 1-->iostream stream error
cate-name-->future
error 1-->broken promise
cate-name-->database
error 1-->exesql error
---------------
connect db fail
0
database
<think>嗯,用户想了解C++中的错误码设计方式。首先,我得回忆一下常见的错误处理机制。C++不像其他语言有异常处理作为主要方式,很多项目,尤其是性能敏感或嵌入式系统,可能更倾向于使用错误码。那错误码设计有哪些方法呢? 首先,传统的错误码返回,比如函数返回一个int,0表示成功,非0表示不同错误。这可能简单,但不够直观,类型也不安全。比如,不同的库可能用相同的int值代表不同错误,容易混淆。 然后是枚举类,enum class,这样类型安全,可以定义不同的错误类型,避免整数值冲突。比如定义一个ErrorCode枚举,包含各种错误情况。但用户可能需要检查每个返回值,容易遗漏,而且错误处理可能让代码显得冗长。 接下来,C++11引入了std::error_code和相关的<system_error>库。这部分可能比较复杂,但标准库支持更好。可以自定义错误类别,继承std::error_category,这样能封装错误码和消息,还能跨库统一处理错误。比如系统错误和自定义错误都通过同样的接口处理,这可能更灵活。 另外,用户可能想知道错误码与异常的结合使用。虽然很多项目禁用异常,但在允许的情况下,可以将错误码转换为异常抛出,或者在捕获异常后转为错误码返回。这需要权衡项目的异常使用策略。 还有使用结构体或类封装更多错误信息,比如错误码、描述、来源等。这对需要详细错误信息的场景有用,但可能增加开销。 需要考虑的问题包括错误码的可读性、可维护性,如何保证全局唯一性,避免重复,以及错误信息的国际化支持。另外,错误码的传递和日志记录也是设计时要考虑的,比如在多层调用中如何保留原始错误信息。 可能用户的项目需要选择适合的方式,比如嵌入式系统可能倾向简单枚举,而大型应用可能用std::error_code。还要考虑团队习惯和现有代码风格,确保一致性。 另外,错误码的设计可能涉及错误处理策略,比如立即处理、传播还是记录。比如函数返回错误码,调用者负责检查并处理,或者累积错误信息向上传递。 可能用户没有明确说是否了解异常以外的错误处理,所以需要涵盖基本和高级方法,并比较优缺点。比如错误码的优缺点:简单高效,但缺乏上下文;异常能自动传播,但有性能开销。 总结的话,应该分点介绍不同方法,举例说明,并给出适用场景和建议,帮助用户根据需求选择合适的设计方式。</think>在 C++ 中,错误码设计是错误处理的重要方式之一,尤其在性能敏感或禁用异常的代码中广泛使用。以下是几种常见的错误码设计方式及其特点: --- ### 1. **传统整型错误码** - **直接返回整型值**(如 `int`),约定 `0` 表示成功,非零值表示不同错误。 - **优点**:简单高效,适合嵌入式或低层系统。 - **缺点**:可读性差,需额外文档解释错误码含义。 ```cpp int ReadFile(const char* path) { if (file_not_found) return 1; if (permission_denied) return 2; return 0; // 成功 } ``` --- ### 2. **枚举类型(Enum/Enum Class)** - **定义明确的错误类型**,利用 `enum class` 提升类型安全性。 - **优点**:代码可读性强,避免整型歧义。 - **缺点**:需显式处理返回值,无法携带额外信息。 ```cpp enum class ErrorCode { Success = 0, FileNotFound, PermissionDenied, InvalidFormat }; ErrorCode ParseData(const std::string& data) { if (data.empty()) return ErrorCode::InvalidFormat; // ... return ErrorCode::Success; } ``` --- ### 3. **标准库 `std::error_code`(C++11 起)** - **结合错误码与错误类别**,支持跨库统一错误处理。 - **优点**:标准化、可扩展,支持自定义错误类别和消息。 - **缺点**:实现稍复杂,需定义错误类别(继承 `std::error_category`)。 ```cpp #include <system_error> enum class ParseError { EmptyData = 1, InvalidChar }; struct ParseErrorCategory : std::error_category { const char* name() const noexcept override { return "parse_error"; } std::string message(int ev) const override { switch (static_cast<ParseError>(ev)) { case ParseError::EmptyData: return "Empty input data"; case ParseError::InvalidChar: return "Invalid character"; default: return "Unknown error"; } } }; const ParseErrorCategory parse_error_category; std::error_code make_error_code(ParseError e) { return {static_cast<int>(e), parse_error_category}; } // 使用示例 std::error_code ReadConfig() { if (data.empty()) return ParseError::EmptyData; return {}; // 成功(默认构造表示无错误) } ``` --- ### 4. **结构体/类封装错误信息** - **自定义结构体携带更多上下文**(如错误码、描述、来源等)。 - **优点**:灵活,支持复杂错误信息。 - **缺点**:内存和性能开销略高。 ```cpp struct Error { int code; std::string message; std::string source; explicit operator bool() const { return code != 0; } }; Error ReadNetworkData() { if (timeout) return {1001, "Connection timeout", "NetworkModule"}; return {0, "", ""}; // 成功 } ``` --- ### 5. **与异常结合使用** - **错误码作为异常的一部分**,通过异常传递错误码。 - **优点**:兼顾错误码的明确性和异常的自动传播。 - **缺点**:需启用异常机制。 ```cpp class DataException : public std::exception { public: DataException(int code, const std::string& msg) : code_(code), msg_(msg) {} const char* what() const noexcept override { return msg_.c_str(); } int code() const { return code_; } private: int code_; std::string msg_; }; void LoadData() { if (invalid_format) throw DataException(500, "Invalid data format"); } ``` --- ### **设计建议** 1. **统一错误码规范**:确保全局唯一性,避免不同模块的冲突。 2. **提供错误码到字符串的转换**(如 `std::error_code` 的 `message()` 方法)。 3. **错误传播与日志**:在多层调用中保留原始错误信息,便于调试。 4. **兼容性**:若与 C 接口交互,可沿用传统整型错误码。 5. **文档化**:明确每个错误码的含义和触发条件。 通过合理设计错误码,可以提升代码健壮性和可维护性,同时平衡性能与易用性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值