langShift

错误处理和异常

错误处理是健壮软件开发的关键环节。C++ 通过异常提供强大的错误处理机制,允许将错误处理代码与正常程序逻辑分离。这与 JavaScript 更常见的返回值和用于异步操作的 try...catch 块有显著不同。

异常处理机制 (try-catch-throw)

在 C++ 中,异常用于信号和处理程序执行期间发生的异常情况或错误。异常处理的核心组件是:

  • throw 用于信号异常情况。当错误发生时,会 throw 一个异常。
  • try 块: 可能发生异常的代码块。此块内的代码会受到监控,以检测异常。
  • catch 块: 处理特定类型异常的代码块。如果在 try 块内抛出异常,程序会跳转到适当的 catch 块。
正在加载...

异常安全保证

在设计可能抛出异常的函数时,考虑异常安全保证非常重要。这些保证描述了如果抛出异常时程序的 상태:

  1. 不抛出保证 (最强): 函数永远不会抛出异常。
  2. 强保证: 如果抛出异常,程序的状态保持不变(就像从未调用过函数一样)。
  3. 基本保证: 如果抛出异常,程序保持在有效状态,但确切状态未指定。
  4. 无保证 (最弱): 如果抛出异常,对程序状态没有任何保证。

错误码 vs. 异常

历史上,C 语言风格的编程通常使用错误码(返回一个特殊值来指示错误)进行错误处理。虽然简单,但这种方法可能导致代码冗长且容易忽略错误。

异常在现代 C++ 中通常用于异常情况,因为:

  • 它们将错误处理逻辑与正常代码流程分离。
  • 它们沿着调用堆栈传播,直到被捕获,防止错误被忽略。
  • 它们可以携带更多关于错误的信息。

RAII (资源获取即初始化) 资源管理

RAII 是 C++ 中用于安全管理资源(如内存、文件句柄、网络连接)的基本惯用语。它指出资源获取应在构造函数中发生,资源释放应在析构函数中发生。当对象超出作用域时(无论是正常情况还是由于异常),其析构函数都会自动调用,确保资源得到正确清理。

这对于异常尤其重要,因为它保证即使异常绕过正常函数出口点,资源也能得到释放。

正在加载...

与 JavaScript 错误处理的比较

JavaScript 主要使用 Error 对象和 try...catch 块进行同步错误处理。对于异步操作,Promise 和 async/awaittry...catch 结合使用很常见。

特性JavaScriptC++
机制Error 对象、throwtry...catch异常 (throwtry...catch)
资源管理自动 (GC)、finally 用于显式清理手动、RAII (资源获取即初始化)
错误类型内置 Error 类型 (例如,TypeErrorReferenceError)、自定义 Error 子类标准异常 (std::runtime_errorstd::bad_alloc)、自定义异常类
传播沿着调用堆栈传播直到被捕获沿着调用堆栈传播直到被捕获
性能对于简单错误通常开销较小如果异常频繁抛出,可能会产生性能开销 (由于栈展开)

异常处理最佳实践

  1. 按值抛出,按引用捕获: 将异常作为对象抛出(按值),并按常量引用捕获(const std::exception&const MyException&)。这可以避免切片和不必要的复制。
  2. 先捕获特定异常: 在更通用的异常类型之前捕获更特定的异常类型。
  3. 使用 std::exception 层次结构:std::exception 或其子类(例如,std::runtime_errorstd::logic_error)派生自定义异常类。
  4. 避免在析构函数中抛出: 从析构函数中抛出异常可能导致 std::terminate,如果另一个异常已经活跃。
  5. 将异常用于异常情况: 不要将异常用于正常程序流程或预期情况。例如,不要使用异常来信号用户输入了无效输入;而是使用验证和返回值。
  6. RAII 是关键: 利用 RAII 确保资源清理,即使发生异常。

常见异常处理陷阱

  • 按值捕获: 如果按基类类型捕获派生异常,可能导致对象切片。
  • 忽略异常: 不捕获异常可能导致程序终止。
  • 过度使用异常: 将异常用于非异常情况可能会损害性能并使代码难以阅读。
  • 未处理所有异常: 通用 catch (...) 可以作为最后的手段,但特定处理程序更好。

练习题:

  1. 描述 trycatchthrow 关键字在 C++ 异常处理中的作用。它们如何协同工作来管理错误?
  2. 什么是 RAII,为什么它被认为是 C++ 中资源管理的基本惯用语?提供一个示例,说明 RAII 如何帮助确保资源清理,即使发生异常。
  3. 比较和对比 C++ 的异常处理机制与 JavaScript 的错误处理。它们在方法上的主要差异是什么?

项目构想:

  • 创建一个简单的 C++ 程序,模拟文件处理工具。实现可能失败的函数(例如,openFilereadFilewriteFile)。使用 C++ 异常处理文件未找到、权限被拒绝或磁盘已满等错误。确保所有资源(文件句柄)都使用 RAII 原则正确关闭,即使发生异常。