Skip to content

C++ 异常处理

概述

异常处理是C++的重要特性,用于处理程序运行时的错误情况。通过try-catch机制,程序可以优雅地处理异常,而不是崩溃终止。异常处理提高了程序的健壮性和可维护性。

⚠️ 异常处理基础

try-catch-throw机制

cpp
#include <iostream>
#include <stdexcept>
#include <string>

class Calculator {
public:
    static double divide(double a, double b) {
        if (b == 0) {
            throw std::invalid_argument("除数不能为零");
        }
        return a / b;
    }
    
    static double squareRoot(double x) {
        if (x < 0) {
            throw std::domain_error("不能计算负数的平方根");
        }
        return std::sqrt(x);
    }
    
    static int factorial(int n) {
        if (n < 0) {
            throw std::invalid_argument("阶乘的参数不能为负数");
        }
        if (n > 20) {
            throw std::overflow_error("阶乘结果太大");
        }
        
        int result = 1;
        for (int i = 1; i <= n; ++i) {
            result *= i;
        }
        return result;
    }
};

int main() {
    std::cout << "=== 异常处理基础 ===" << std::endl;
    
    // 正常情况
    try {
        double result = Calculator::divide(10, 2);
        std::cout << "10 / 2 = " << result << std::endl;
        
        result = Calculator::squareRoot(16);
        std::cout << "sqrt(16) = " << result << std::endl;
        
        int fact = Calculator::factorial(5);
        std::cout << "5! = " << fact << std::endl;
        
    } catch (const std::exception& e) {
        std::cout << "异常: " << e.what() << std::endl;
    }
    
    // 异常情况
    std::cout << "\n--- 测试异常情况 ---" << std::endl;
    
    try {
        Calculator::divide(10, 0);  // 除零异常
    } catch (const std::invalid_argument& e) {
        std::cout << "捕获invalid_argument: " << e.what() << std::endl;
    }
    
    try {
        Calculator::squareRoot(-4);  // 域错误
    } catch (const std::domain_error& e) {
        std::cout << "捕获domain_error: " << e.what() << std::endl;
    }
    
    try {
        Calculator::factorial(25);  // 溢出错误
    } catch (const std::overflow_error& e) {
        std::cout << "捕获overflow_error: " << e.what() << std::endl;
    }
    
    return 0;
}

多重catch和异常层次

cpp
#include <iostream>
#include <stdexcept>
#include <memory>

class BankAccount {
private:
    double balance_;
    std::string accountNumber_;
    
public:
    BankAccount(const std::string& number, double balance) 
        : accountNumber_(number), balance_(balance) {}
    
    void withdraw(double amount) {
        if (amount <= 0) {
            throw std::invalid_argument("取款金额必须大于0");
        }
        if (amount > balance_) {
            throw std::runtime_error("余额不足");
        }
        balance_ -= amount;
        std::cout << "取款 " << amount << " 成功,余额: " << balance_ << std::endl;
    }
    
    void deposit(double amount) {
        if (amount <= 0) {
            throw std::invalid_argument("存款金额必须大于0");
        }
        balance_ += amount;
        std::cout << "存款 " << amount << " 成功,余额: " << balance_ << std::endl;
    }
    
    double getBalance() const { return balance_; }
};

void performBankOperations() {
    BankAccount account("123456", 1000.0);
    
    try {
        account.deposit(500);
        account.withdraw(200);
        account.withdraw(2000);  // 余额不足
        
    } catch (const std::invalid_argument& e) {
        std::cout << "参数错误: " << e.what() << std::endl;
    } catch (const std::runtime_error& e) {
        std::cout << "运行时错误: " << e.what() << std::endl;
    } catch (const std::exception& e) {
        std::cout << "其他异常: " << e.what() << std::endl;
    } catch (...) {
        std::cout << "未知异常" << std::endl;
    }
}

int main() {
    std::cout << "=== 多重catch处理 ===" << std::endl;
    performBankOperations();
    
    return 0;
}

🎯 自定义异常

创建异常类

cpp
#include <iostream>
#include <stdexcept>
#include <string>

// 自定义异常基类
class FileException : public std::exception {
protected:
    std::string message_;
    std::string filename_;
    
public:
    FileException(const std::string& message, const std::string& filename)
        : message_(message), filename_(filename) {}
    
    const char* what() const noexcept override {
        return message_.c_str();
    }
    
    const std::string& getFilename() const { return filename_; }
    
    virtual std::string getFullMessage() const {
        return message_ + " (文件: " + filename_ + ")";
    }
};

// 具体异常类
class FileNotFound : public FileException {
public:
    FileNotFound(const std::string& filename)
        : FileException("文件未找到", filename) {}
};

class FileReadError : public FileException {
public:
    FileReadError(const std::string& filename)
        : FileException("文件读取错误", filename) {}
};

class FileWriteError : public FileException {
public:
    FileWriteError(const std::string& filename)
        : FileException("文件写入错误", filename) {}
};

// 使用自定义异常的类
class FileManager {
public:
    static std::string readFile(const std::string& filename) {
        if (filename.empty()) {
            throw std::invalid_argument("文件名不能为空");
        }
        
        if (filename == "nonexistent.txt") {
            throw FileNotFound(filename);
        }
        
        if (filename == "corrupted.txt") {
            throw FileReadError(filename);
        }
        
        return "文件内容: " + filename;
    }
    
    static void writeFile(const std::string& filename, const std::string& content) {
        if (filename.empty()) {
            throw std::invalid_argument("文件名不能为空");
        }
        
        if (filename == "readonly.txt") {
            throw FileWriteError(filename);
        }
        
        std::cout << "写入文件 " << filename << ": " << content << std::endl;
    }
};

int main() {
    std::cout << "=== 自定义异常 ===" << std::endl;
    
    std::vector<std::string> testFiles = {
        "normal.txt",
        "nonexistent.txt", 
        "corrupted.txt",
        ""
    };
    
    for (const auto& filename : testFiles) {
        try {
            std::cout << "\n尝试读取: " << (filename.empty() ? "[空文件名]" : filename) << std::endl;
            std::string content = FileManager::readFile(filename);
            std::cout << "成功: " << content << std::endl;
            
        } catch (const FileException& e) {
            std::cout << "文件异常: " << e.getFullMessage() << std::endl;
        } catch (const std::invalid_argument& e) {
            std::cout << "参数异常: " << e.what() << std::endl;
        }
    }
    
    // 测试写入异常
    try {
        FileManager::writeFile("readonly.txt", "测试内容");
    } catch (const FileWriteError& e) {
        std::cout << "\n写入异常: " << e.getFullMessage() << std::endl;
    }
    
    return 0;
}

🔄 异常安全性

RAII和异常安全

cpp
#include <iostream>
#include <memory>
#include <stdexcept>

// RAII资源管理类
class Resource {
private:
    std::string name_;
    int* data_;
    
public:
    explicit Resource(const std::string& name) : name_(name) {
        data_ = new int[100];
        std::cout << "资源 " << name_ << " 已分配" << std::endl;
    }
    
    ~Resource() {
        delete[] data_;
        std::cout << "资源 " << name_ << " 已释放" << std::endl;
    }
    
    // 禁止拷贝
    Resource(const Resource&) = delete;
    Resource& operator=(const Resource&) = delete;
    
    void doWork() {
        std::cout << "资源 " << name_ << " 正在工作" << std::endl;
        // 可能抛出异常
        if (name_ == "BadResource") {
            throw std::runtime_error("工作失败");
        }
    }
    
    const std::string& getName() const { return name_; }
};

// 异常安全的函数
void safeFunction() {
    std::cout << "\n=== 异常安全函数 ===" << std::endl;
    
    Resource res1("Resource1");
    Resource res2("Resource2");
    
    try {
        res1.doWork();
        res2.doWork();
        std::cout << "所有资源工作完成" << std::endl;
        
    } catch (const std::exception& e) {
        std::cout << "异常: " << e.what() << std::endl;
        // 资源会自动释放(RAII)
    }
    // res1和res2在这里自动析构
}

// 不安全的函数(演示)
void unsafeFunction() {
    std::cout << "\n=== 不安全函数(演示) ===" << std::endl;
    
    int* data1 = new int[100];
    int* data2 = new int[100];
    
    try {
        std::cout << "分配了两个数组" << std::endl;
        
        // 如果这里抛出异常,内存会泄漏
        throw std::runtime_error("模拟异常");
        
        delete[] data1;  // 不会执行
        delete[] data2;  // 不会执行
        
    } catch (const std::exception& e) {
        std::cout << "异常: " << e.what() << std::endl;
        // 内存泄漏!
        delete[] data1;  // 手动清理
        delete[] data2;
    }
}

// 使用智能指针的安全函数
void smartPointerFunction() {
    std::cout << "\n=== 智能指针安全函数 ===" << std::endl;
    
    auto res1 = std::make_unique<Resource>("SmartResource1");
    auto res2 = std::make_unique<Resource>("SmartResource2");
    
    try {
        res1->doWork();
        res2->doWork();
        std::cout << "智能指针管理的资源工作完成" << std::endl;
        
    } catch (const std::exception& e) {
        std::cout << "异常: " << e.what() << std::endl;
        // 智能指针自动释放资源
    }
}

int main() {
    std::cout << "=== 异常安全性 ===" << std::endl;
    
    safeFunction();
    unsafeFunction();
    smartPointerFunction();
    
    return 0;
}

📋 异常处理最佳实践

异常处理指导原则

cpp
#include <iostream>
#include <stdexcept>
#include <vector>

class BestPracticesDemo {
public:
    // 1. 在构造函数中使用异常
    class SafeArray {
    private:
        std::vector<int> data_;
        
    public:
        SafeArray(size_t size) {
            if (size == 0) {
                throw std::invalid_argument("数组大小必须大于0");
            }
            if (size > 1000000) {
                throw std::overflow_error("数组太大");
            }
            data_.resize(size);
            std::cout << "创建大小为 " << size << " 的数组" << std::endl;
        }
        
        int& at(size_t index) {
            if (index >= data_.size()) {
                throw std::out_of_range("索引越界");
            }
            return data_[index];
        }
        
        size_t size() const noexcept { return data_.size(); }
    };
    
    // 2. noexcept规范
    static int add(int a, int b) noexcept {
        return a + b;  // 保证不抛异常
    }
    
    static void printMessage(const std::string& msg) noexcept {
        try {
            std::cout << msg << std::endl;
        } catch (...) {
            // 吞掉所有异常,保证noexcept
        }
    }
    
    // 3. 异常规范的使用
    static void demonstrateExceptionSafety() {
        std::cout << "=== 异常安全等级 ===" << std::endl;
        
        try {
            SafeArray arr(10);
            
            // 基本保证:如果异常发生,对象处于有效状态
            arr.at(5) = 42;
            
            // 强异常保证:要么完全成功,要么完全回滚
            std::cout << "数组操作成功" << std::endl;
            
            // 尝试访问无效索引
            arr.at(15) = 100;  // 会抛出异常
            
        } catch (const std::out_of_range& e) {
            std::cout << "边界异常: " << e.what() << std::endl;
        } catch (const std::exception& e) {
            std::cout << "其他异常: " << e.what() << std::endl;
        }
    }
};

int main() {
    std::cout << "=== 异常处理最佳实践 ===" << std::endl;
    
    // 测试构造函数异常
    try {
        BestPracticesDemo::SafeArray invalidArray(0);
    } catch (const std::exception& e) {
        std::cout << "构造异常: " << e.what() << std::endl;
    }
    
    // noexcept函数调用
    int result = BestPracticesDemo::add(3, 4);
    std::cout << "noexcept加法: " << result << std::endl;
    
    BestPracticesDemo::printMessage("这是noexcept消息");
    
    // 异常安全演示
    BestPracticesDemo::demonstrateExceptionSafety();
    
    std::cout << "\n=== 异常处理原则 ===" << std::endl;
    std::cout << "1. 使用RAII管理资源" << std::endl;
    std::cout << "2. 优先使用标准异常类" << std::endl;
    std::cout << "3. 不要在析构函数中抛异常" << std::endl;
    std::cout << "4. 适当使用noexcept规范" << std::endl;
    std::cout << "5. 异常信息要清晰明确" << std::endl;
    
    return 0;
}

总结

异常处理是C++程序健壮性的重要保障:

异常类型层次

  • std::exception: 基础异常类
  • std::logic_error: 逻辑错误
  • std::runtime_error: 运行时错误
  • 自定义异常: 特定领域异常

异常安全等级

等级说明特点
无保证可能产生资源泄漏不推荐
基本保证对象处于有效状态最低要求
强保证要么成功要么回滚推荐
不抛异常保证不抛出异常最高级别

最佳实践

  • 使用RAII管理资源
  • 合理设计异常层次
  • 适当使用noexcept
  • 避免在析构函数中抛异常
  • 提供清晰的异常信息

异常处理让程序能够优雅地处理错误情况,提高代码的可靠性和用户体验。

本站内容仅供学习和研究使用。