C++ 的异常处理机制是一种用于处理程序运行时错误的结构化方法,通过分离正常逻辑与错误处理代码,提高代码的可读性和可维护性。以下是其核心组成部分和工作原理的详细说明:
1. 异常处理的三大关键字
1.1 try
块
-
作用:包裹可能抛出异常的代码段。
-
语法:
try { // 可能抛出异常的代码 }
1.2 throw
表达式
-
作用:抛出异常对象(可以是任意类型,但通常继承自
std::exception
)。 -
语法:
throw exception_object;
1.3 catch
块
-
作用:捕获并处理特定类型的异常。
-
语法:
catch (ExceptionType1& e) { // 处理 ExceptionType1 异常 } catch (ExceptionType2& e) { // 处理 ExceptionType2 异常 } catch (...) { // 捕获所有异常 // 处理未知异常 }
2. 异常处理流程
-
抛出异常:代码执行到
throw
时,立即停止当前函数,开始栈展开(Stack Unwinding)。 -
栈展开:
-
析构当前作用域的局部对象(按构造逆序)。
-
沿调用链向上查找匹配的
catch
块。
-
-
捕获异常:找到第一个匹配的
catch
块后执行其代码。 -
未捕获异常:若未找到匹配的
catch
块,调用std::terminate()
终止程序。
3. 异常类型与捕获方式
3.1 标准异常类
C++ 标准库定义了一组异常类(位于 <stdexcept>
头文件),均继承自 std::exception
:
-
std::logic_error
:程序逻辑错误(如std::invalid_argument
)。 -
std::runtime_error
:运行时错误(如std::overflow_error
)。 -
自定义异常通常继承自
std::exception
:class MyException : public std::exception { public: const char* what() const noexcept override { return "My custom exception"; } };
3.2 捕获方式
-
按值捕获:拷贝异常对象(可能引发切片问题)。
-
按引用捕获:避免拷贝,保留多态性(推荐)。
-
按指针捕获:需手动管理内存(不推荐)。
-
捕获所有异常:
catch (...)
(通常用于日志记录或资源清理)。
4. 异常安全性
函数在抛出异常时需保证资源不泄漏,分为三个级别:
-
基本保证(Basic Guarantee):异常发生后,程序处于合法状态。
-
强保证(Strong Guarantee):操作要么完全成功,要么回滚到操作前的状态(事务语义)。
-
不抛保证(No-throw Guarantee):承诺不抛出任何异常(用
noexcept
标记)。
5. 关键机制与注意事项
5.1 noexcept
关键字
-
作用:声明函数不会抛出异常,帮助编译器优化代码。
-
语法:
void func() noexcept; // C++11 起
5.2 栈展开与析构
-
RAII(Resource Acquisition Is Initialization):依赖对象的析构函数自动释放资源(如智能指针、文件句柄)。
-
避免在析构函数中抛出异常:可能导致程序终止。
5.3 性能影响
-
零开销原则:无异常时代码无额外开销。
-
抛出异常时开销较大:涉及栈展开和类型匹配,避免频繁使用异常处理常规逻辑。
6. 代码示例
6.1 基本用法
#include <iostream>
#include <stdexcept>
void riskyOperation(int value) {
if (value < 0) {
throw std::invalid_argument("Value cannot be negative");
}
// 其他操作
}
int main() {
try {
riskyOperation(-5);
} catch (const std::invalid_argument& e) {
std::cerr << "Error: " << e.what() << std::endl;
} catch (...) {
std::cerr << "Unknown error occurred" << std::endl;
}
return 0;
}
6.2 自定义异常
#include <exception>
#include <string>
class NetworkError : public std::exception {
private:
std::string message;
public:
NetworkError(const std::string& msg) : message(msg) {}
const char* what() const noexcept override {
return message.c_str();
}
};
void connectToServer() {
throw NetworkError("Connection timeout");
}
7. 最佳实践
-
优先使用标准异常类型,保持异常层次清晰。
-
按引用捕获异常,避免对象切片和多态丢失。
-
利用 RAII 管理资源,确保异常安全。
-
避免在构造函数中抛出异常,除非能完全清理资源。
-
用
noexcept
标记不会抛出异常的函数,提升性能。
总结
C++ 异常处理通过 try
/catch
/throw
提供了一种结构化的错误管理机制,结合 RAII 和标准异常类,可以有效提升代码的健壮性。合理使用异常处理需权衡性能与安全性,遵循异常安全等级和最佳实践。