C++ 异常处理基础
C++异常处理是C++编程语言中用于处理运行时错误的一种机制,它允许程序在检测到错误时将控制权转移到专门的错误处理代码块,从而提高程序的健壮性和可维护性。异常处理的核心思想是将错误检测与错误处理分离,使主逻辑代码更加清晰。
异常处理的基本概念[编辑 | 编辑源代码]
在C++中,异常是指程序在运行时发生的意外情况,例如:
- 除数为零
- 数组越界访问
- 内存分配失败
- 文件打开失败
异常处理机制由三个关键字组成:
- throw - 抛出异常
- try - 检测异常
- catch - 捕获并处理异常
异常处理流程[编辑 | 编辑源代码]
基本语法[编辑 | 编辑源代码]
以下是异常处理的基本语法结构:
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`基类:
常用标准异常类:
- `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++实现中,异常处理的"无异常"路径通常没有额外开销。
数学公式示例[编辑 | 编辑源代码]
异常处理概率可以表示为:
总结[编辑 | 编辑源代码]
C++异常处理提供了一种结构化的错误处理机制,能够:
- 将错误处理代码与正常逻辑分离
- 自动传播错误信息到适当的处理位置
- 支持多级错误处理
- 提供标准化的错误报告机制
正确使用异常处理可以显著提高代码的健壮性和可维护性,但需要注意异常安全和性能影响。