跳转到内容

C++ 异常处理基础

来自代码酷


C++异常处理是C++编程语言中用于处理运行时错误的一种机制,它允许程序在检测到错误时将控制权转移到专门的错误处理代码块,从而提高程序的健壮性和可维护性。异常处理的核心思想是将错误检测与错误处理分离,使主逻辑代码更加清晰。

异常处理的基本概念[编辑 | 编辑源代码]

在C++中,异常是指程序在运行时发生的意外情况,例如:

  • 除数为零
  • 数组越界访问
  • 内存分配失败
  • 文件打开失败

异常处理机制由三个关键字组成:

  • throw - 抛出异常
  • try - 检测异常
  • catch - 捕获并处理异常

异常处理流程[编辑 | 编辑源代码]

graph TD A[正常执行] --> B{是否发生异常?} B -->|是| C[throw抛出异常] B -->|否| D[继续执行] C --> E[查找匹配的catch块] E --> F[执行catch块] F --> G[继续执行或终止]

基本语法[编辑 | 编辑源代码]

以下是异常处理的基本语法结构:

try {
    // 可能抛出异常的代码
    if (error_condition) {
        throw exception_object; // 抛出异常
    }
    // 其他代码
}
catch (exception_type1& e) {
    // 处理类型1的异常
}
catch (exception_type2& e) {
    // 处理类型2的异常
}
catch (...) {
    // 处理所有其他类型的异常
}

基本示例[编辑 | 编辑源代码]

示例1:除零异常[编辑 | 编辑源代码]

#include <iostream>
#include <stdexcept>

double divide(int numerator, int denominator) {
    if (denominator == 0) {
        throw std::runtime_error("Division by zero!");
    }
    return static_cast<double>(numerator) / denominator;
}

int main() {
    try {
        double result = divide(10, 0);
        std::cout << "Result: " << result << std::endl;
    }
    catch (const std::runtime_error& e) {
        std::cerr << "Error: " << e.what() << std::endl;
    }
    return 0;
}

输出:

Error: Division by zero!

解释: 1. 当`divide()`函数检测到除数为零时,使用`throw`抛出`runtime_error`异常 2. `main()`函数中的`try`块捕获到这个异常 3. 匹配的`catch`块处理异常,打印错误信息

示例2:多类型异常处理[编辑 | 编辑源代码]

#include <iostream>
#include <stdexcept>

void process_input(int value) {
    if (value < 0) {
        throw std::out_of_range("Negative value not allowed");
    }
    if (value > 100) {
        throw std::overflow_error("Value too large");
    }
    std::cout << "Processing value: " << value << std::endl;
}

int main() {
    try {
        process_input(-5);
    }
    catch (const std::out_of_range& e) {
        std::cerr << "Range error: " << e.what() << std::endl;
    }
    catch (const std::overflow_error& e) {
        std::cerr << "Overflow error: " << e.what() << std::endl;
    }
    catch (...) {
        std::cerr << "Unknown error occurred" << std::endl;
    }
    return 0;
}

输出:

Range error: Negative value not allowed

标准异常类[编辑 | 编辑源代码]

C++标准库提供了一系列异常类,都继承自`std::exception`基类:

classDiagram exception <|-- logic_error exception <|-- runtime_error logic_error <|-- invalid_argument logic_error <|-- out_of_range logic_error <|-- length_error runtime_error <|-- range_error runtime_error <|-- overflow_error runtime_error <|-- underflow_error

常用标准异常类:

  • `std::exception` - 所有标准异常的基类
  • `std::logic_error` - 程序逻辑错误
  • `std::runtime_error` - 运行时错误
  • `std::bad_alloc` - 内存分配失败
  • `std::bad_cast` - 动态类型转换失败

自定义异常[编辑 | 编辑源代码]

可以创建自定义异常类来满足特定需求:

#include <iostream>
#include <string>
#include <exception>

class MyException : public std::exception {
private:
    std::string message;
public:
    MyException(const std::string& msg) : message(msg) {}
    const char* what() const noexcept override {
        return message.c_str();
    }
};

void test_function(int x) {
    if (x == 0) {
        throw MyException("Custom exception: x cannot be zero");
    }
}

int main() {
    try {
        test_function(0);
    }
    catch (const MyException& e) {
        std::cerr << "Caught: " << e.what() << std::endl;
    }
    return 0;
}

输出:

Caught: Custom exception: x cannot be zero

异常安全[编辑 | 编辑源代码]

编写异常安全的代码需要考虑以下几点: 1. 基本保证 - 发生异常时程序处于有效状态 2. 强保证 - 操作要么完全成功,要么完全回滚 3. 不抛出保证 - 保证不会抛出异常

资源管理示例[编辑 | 编辑源代码]

使用RAII(Resource Acquisition Is Initialization)技术确保资源安全:

#include <iostream>
#include <memory>

class FileHandler {
private:
    std::unique_ptr<FILE, decltype(&fclose)> file;
public:
    FileHandler(const char* filename, const char* mode)
        : file(fopen(filename, mode), &fclose) {
        if (!file) {
            throw std::runtime_error("Failed to open file");
        }
    }
    void write(const std::string& data) {
        if (fputs(data.c_str(), file.get()) == EOF) {
            throw std::runtime_error("Failed to write to file");
        }
    }
};

int main() {
    try {
        FileHandler fh("test.txt", "w");
        fh.write("Hello, World!");
    }
    catch (const std::exception& e) {
        std::cerr << "Error: " << e.what() << std::endl;
    }
    return 0;
}

解释:

  • 使用`unique_ptr`管理文件指针,确保异常发生时文件会被正确关闭
  • 构造函数和写操作都可能抛出异常

最佳实践[编辑 | 编辑源代码]

1. 只在真正异常情况下使用异常,不要用于常规控制流 2. 按引用捕获异常(通常是const引用) 3. 从`std::exception`派生自定义异常类 4. 确保析构函数不会抛出异常 5. 在catch块中按从具体到一般的顺序排列 6. 考虑使用`noexcept`标记不会抛出异常的函数

性能考虑[编辑 | 编辑源代码]

异常处理通常比常规错误检查(如返回错误码)有更高的运行时开销,因为:

  • 需要维护异常处理表
  • 栈展开过程可能较复杂
  • 在异常路径上编译器优化受限

但在现代C++实现中,异常处理的"无异常"路径通常没有额外开销。

数学公式示例[编辑 | 编辑源代码]

异常处理概率可以表示为:

P(exception)=异常情况数量总执行路径数量

总结[编辑 | 编辑源代码]

C++异常处理提供了一种结构化的错误处理机制,能够:

  • 将错误处理代码与正常逻辑分离
  • 自动传播错误信息到适当的处理位置
  • 支持多级错误处理
  • 提供标准化的错误报告机制

正确使用异常处理可以显著提高代码的健壮性和可维护性,但需要注意异常安全和性能影响。