引言:为什么错误处理是 C++ 可靠代码的基石
任何与外部世界(文件、网络、用户输入)交互的程序都不可避免地会遇到异常情况。在 C++ 中,try/catch 机制是优雅且可控地管理此类情况的主要工具。在本文中,我们将从基本结构到高级技术,包括最佳实践和陷阱,全面解析 C++ 中的错误处理。
1. 基础:try、catch 和 throw
C++ 中的异常机制建立在三个关键关键字之上:try、catch 和 throw。
1.1. try 和 catch 块
try 块包含可能抛出异常的代码。catch 块捕获该异常并进行处理。
#include <iostream>#include <stdexcept>
int main() { try { // 可能引发错误的代码 int age = -5; if (age < 0) { throw std::invalid_argument("年龄不能为负数"); } std::cout << "年龄: " << age << std::endl; } catch (const std::invalid_argument& e) { std::cerr << "错误: " << e.what() << std::endl; } return 0;}1.2. 异常类型
在 C++ 中,可以抛出任何类型的异常:数字、字符串、类对象。但建议使用 <stdexcept> 中的标准异常类,或创建继承自 std::exception 的自定义类。
- 标准异常:
std::runtime_error、std::logic_error、std::out_of_range等。 - 自定义异常:通过继承
std::exception创建。
class MyException : public std::exception {public: const char* what() const noexcept override { return "我的自定义异常"; }};1.3. 多个 catch 块
您可以在单独的 catch 块中捕获不同类型的异常。顺序很重要:更具体的异常应放在前面。
try { // 代码} catch (const std::invalid_argument& e) { // 处理 invalid_argument} catch (const std::exception& e) { // 处理所有其他 std 异常} catch (...) { // catch-all:捕获上面未捕获的所有异常}2. 高级技术:栈展开与 RAII
2.1. 栈展开(Stack Unwinding)
当抛出异常时,C++ 会自动销毁从 throw 点到相应 catch 路径上的所有局部对象。这个过程称为栈展开。它保证为在 try 块中创建的所有对象调用析构函数。
class Resource {public: Resource() { std::cout << "资源已创建"; } ~Resource() { std::cout << "资源已销毁"; }};
void riskyFunction() { Resource res; throw std::runtime_error("出问题了");}
int main() { try { riskyFunction(); } catch (const std::exception& e) { std::cerr << e.what() << std::endl; } // 输出:资源已创建 -> 资源已销毁 -> 出问题了}2.2. RAII(资源获取即初始化)
RAII 是 C++ 中与错误处理密切相关的关键惯用法。其思想是:资源管理(内存、文件、互斥锁)与对象的生命周期绑定。即使发生异常,析构函数也会自动释放资源。
- 智能指针:
std::unique_ptr、std::shared_ptr