位置: 文档库 > C/C++ > C++程序创建自定义异常

C++程序创建自定义异常

QuantumPione59 上传于 2020-04-02 12:30

《C++程序创建自定义异常》

在C++程序设计中,异常处理是构建健壮系统的重要机制。标准库提供的std::exception及其派生类(如std::runtime_errorstd::logic_error)虽然能覆盖大部分场景,但当需要表达特定业务逻辑错误时,自定义异常类显得尤为必要。本文将系统阐述如何创建符合C++规范的自定义异常,并探讨其设计原则与最佳实践。

一、异常处理基础回顾

C++通过try/catch/throw机制实现异常处理。标准异常类位于头文件,例如:

#include 

void validateInput(int value) {
    if (value 

该示例展示了标准异常的使用方式,但存在两个局限性:错误信息固定且无法携带业务上下文数据。

二、自定义异常设计原则

创建自定义异常需遵循以下原则:

  1. 继承体系:必须直接或间接继承自std::exception
  2. 虚析构函数:确保多态删除安全
  3. 异常安全:构造函数不应抛出异常
  4. 信息丰富性:提供有意义的错误描述

三、基础自定义异常实现

最简单的自定义异常只需重写what()方法:

#include 
#include 

class DatabaseError : public std::exception {
    std::string msg;
public:
    explicit DatabaseError(const std::string& errorMsg) 
        : msg("Database Error: " + errorMsg) {}
    
    const char* what() const noexcept override {
        return msg.c_str();
    }
};

使用示例:

void connectToDatabase() {
    bool success = false; // 模拟连接失败
    if (!success) {
        throw DatabaseError("Connection timeout");
    }
}

int main() {
    try {
        connectToDatabase();
    } catch (const DatabaseError& e) {
        std::cerr 

四、进阶设计:携带上下文数据

业务异常常需携带额外信息,如错误代码、时间戳等:

#include 
#include 
#include 

class HttpError : public std::exception {
    int statusCode;
    std::string url;
    std::time_t timestamp;
    mutable std::string cachedMsg;
    
    std::string generateMessage() const {
        std::ostringstream oss;
        oss 

这种设计允许捕获后访问具体错误属性:

try {
    // 模拟HTTP请求
    throw HttpError(404, "https://api.example.com/data");
} catch (const HttpError& e) {
    std::cout 

五、异常层次结构设计

对于复杂系统,建议构建异常层次体系:

// 基础业务异常
class BusinessException : public std::exception {
protected:
    std::string component;
public:
    explicit BusinessException(const std::string& comp)
        : component(comp) {}
    
    const char* what() const noexcept override {
        return ("Business Error in " + component).c_str();
    }
};

// 支付模块异常
class PaymentError : public BusinessException {
    std::string transactionId;
    double amount;
public:
    PaymentError(const std::string& tid, double amt)
        : BusinessException("Payment"), transactionId(tid), amount(amt) {}
    
    const char* what() const noexcept override {
        static std::string msg;
        msg = "Payment Error [TXN:" + transactionId + 
              "] Amount:" + std::to_string(amount);
        return msg.c_str();
    }
    
    double getAmount() const noexcept { return amount; }
};

这种设计允许针对不同模块进行特异性捕获:

try {
    processPayment("TXN123", 100.50);
} catch (const PaymentError& e) {
    std::cout 

六、最佳实践与注意事项

1. 异常规范:C++11起推荐使用noexcept标记不抛出异常的函数

class NonThrowingException : public std::exception {
public:
    const char* what() const noexcept override { 
        return "This should never throw"; 
    }
};

2. 内存管理:避免在异常构造函数中分配动态内存

// 不良示例 - 可能抛出std::bad_alloc
class BadMemoryException : public std::exception {
    char* msg;
public:
    BadMemoryException() {
        msg = new char[100]; // 可能抛出异常
        strcpy(msg, "Memory allocation failed");
    }
    // ...
};

3. 性能考虑:异常处理应保留给真正异常情况,避免用作流程控制

4. 跨模块异常:当异常需要跨越DLL边界时,确保使用相同的C++运行时

七、与标准库的协同

自定义异常可与标准异常组合使用:

#include 
#include 

class FileAccessError : public std::runtime_error {
    std::filesystem::path filePath;
public:
    FileAccessError(const std::filesystem::path& p, 
                   const std::string& msg)
        : std::runtime_error(msg), filePath(p) {}
    
    const std::filesystem::path& getPath() const noexcept {
        return filePath;
    }
};

void readFile(const std::filesystem::path& p) {
    std::ifstream file(p);
    if (!file) {
        throw FileAccessError(p, "Failed to open file");
    }
    // 文件操作...
}

八、现代C++特性应用

C++17引入的std::variantstd::expected提供了替代方案,但异常处理在以下场景仍不可替代:

  • 跨调用栈的错误传播
  • 需要强制处理的错误情况
  • 资源获取即初始化(RAII)失败

九、完整示例:订单处理系统

#include 
#include 
#include 
#include 

// 基础异常
class OrderException : public std::exception {
    std::string orderId;
protected:
    explicit OrderException(const std::string& id)
        : orderId(id) {}
    
    std::string prefix() const {
        return "Order " + orderId + " Error: ";
    }
public:
    const std::string& getOrderId() const noexcept {
        return orderId;
    }
};

// 库存不足异常
class InventoryException : public OrderException {
    int required;
    int available;
public:
    InventoryException(const std::string& id, int req, int avail)
        : OrderException(id), required(req), available(avail) {}
    
    const char* what() const noexcept override {
        static std::string msg;
        msg = prefix() + "Insufficient inventory. Required: " + 
              std::to_string(required) + ", Available: " + 
              std::to_string(available);
        return msg.c_str();
    }
    
    int getRequired() const noexcept { return required; }
    int getAvailable() const noexcept { return available; }
};

// 支付失败异常
class PaymentException : public OrderException {
    double amount;
    std::string reason;
public:
    PaymentException(const std::string& id, double amt, const std::string& r)
        : OrderException(id), amount(amt), reason(r) {}
    
    const char* what() const noexcept override {
        static std::string msg;
        msg = prefix() + "Payment failed. Amount: " + 
              std::to_string(amount) + ", Reason: " + reason;
        return msg.c_str();
    }
    
    double getAmount() const noexcept { return amount; }
    const std::string& getReason() const noexcept { return reason; }
};

class OrderProcessor {
    std::vector inventory = {10, 5, 8}; // 模拟库存
public:
    void processOrder(const std::string& orderId, 
                     const std::vector& quantities) {
        // 检查库存
        for (size_t i = 0; i  inventory[i]) {
                throw InventoryException(orderId, quantities[i], inventory[i]);
            }
        }
        
        // 模拟支付
        bool paymentSuccess = false; // 模拟支付失败
        if (!paymentSuccess) {
            double total = 0; // 计算总价(简化)
            throw PaymentException(orderId, total, "Card declined");
        }
        
        // 成功处理...
        std::cout  orderItems = {3, 2, 1};
    
    try {
        processor.processOrder("ORD123", orderItems);
    } catch (const InventoryException& e) {
        std::cerr 

十、总结

自定义异常是C++错误处理的重要工具,合理设计可显著提升代码可维护性。关键要点包括:

  1. 始终继承自std::exception
  2. 提供有意义的错误信息和上下文数据
  3. 构建合理的异常层次结构
  4. 遵循异常安全准则
  5. 与标准异常协同使用

通过系统化的异常设计,开发者能够创建出既符合C++规范又能准确表达业务语义的错误处理机制,为构建健壮的C++应用程序奠定基础。

关键词:C++、自定义异常、异常处理继承体系、异常安全、标准库、业务逻辑、错误上下文RAII现代C++

简介:本文详细阐述C++中自定义异常的设计与实现,涵盖基础实现、进阶设计、层次结构、最佳实践等内容,通过完整示例展示如何创建符合业务需求的异常类,提升程序健壮性和可维护性。