C++ 中的栈展开

2025 年 5 月 24 日 | 4 分钟阅读

在本文中,我们将讨论 C++ 中的堆栈展开以及几种方法和一个示例。

C++ 中的堆栈展开是什么?

当 C++ 中抛出异常时,会发生一个称为堆栈展开的过程。当异常发生时,C++ 运行时系统开始解开或展开函数调用堆栈,以识别能够管理异常的适当 catch 子句。此方法一直持续到找到合适的 catch 块,或者如果未找到任何 catch 块,则直到程序结束。

1. 异常抛出

C++ 中的 throw 关键字用于抛出异常。任何类型,包括用户定义类型、内置类型和对象,都可以是异常。

程序执行期间出现的错误或异常情况通常是抛出异常的原因。

2. Try-Catch 块

C++ 提供 try-catch 块用于处理异常。catch 块描述如何处理特定异常类型,而 try 块包含可能抛出异常的代码。

3. 堆栈展开过程

C++ 运行时系统会查找一个合适的 catch 块来处理在 try 块内抛出的异常。

随着控制流沿着调用堆栈向上攀升,函数调用堆栈被展开(也称为解卷),堆栈中每个函数的局部变量都会被销毁。

它们在构造时,局部变量的析构函数以相反的顺序被调用。

4. RAII 和析构函数

堆栈展开过程对于基于 RAII(资源获取即初始化)原则的资源管理至关重要。

在堆栈展开期间,具有自动存储持续时间(局部变量)的对象的析构函数会被调用,以确保适当的资源清理。

5. 终止或 std::terminate

如果在堆栈展开过程中任何 catch 块都未捕获到异常,则会调用 std::terminate 函数,从而导致程序终止。

6. 清理和资源管理

尽管存在异常,堆栈展开仍提供了一种通过释放内存、关闭文件和释放其他资源来进行清理的方法。

7. 自定义异常类

用户可以继承任何其他异常类型或 std::exception 来构造自己的异常类。它使得异常处理更加有组织和明智。

示例

让我们举一个例子来说明 C++ 中的堆栈展开。

输出

Entering try block in main.
Caught an exception: MyDerivedException1 occurred!
The program continues after the catch block.   

说明

1. 包含头文件

这些是典型的 C++ 头文件。标准异常类存储在 <stdexcept> 中,而输入/输出操作存储在 <iostream> 中。

2. 自定义异常的基类

一个名为 MyBaseException 的自定义异常类派生自 std::exception。

它重写了 what() 函数以提供唯一的错误消息。

3. 派生异常类

MyBaseException 是 MyDerivedException1 和 MyDerivedException2 的父类。

它们对应的错误消息优先于 what() 函数。

4. 可能抛出异常的函数

函数 bar 和 foo 分别有可能抛出 MyDerivedException1 和 MyDerivedException2 类型的异常。

5. Main 函数

main 函数中有一个 try 块。

当调用 foo 时,它会抛出 MyDerivedException1,从而阻止 bar 的运行。

一个 catch 块捕获 MyBaseException 类型或其派生类的异常。

catch 块使用 ex.what() 打印有关捕获到的异常的详细信息。

结论

总之,此代码说明了在 try-catch 块中利用自定义异常类(MyBaseException、MyDerivedException1 和 MyDerivedException2)。它通过演示 catch 块如何捕获基类类型的异常来展示堆栈展开过程中的多态行为。catch 块中打印了与捕获的异常对应的错误消息。catch 块表明堆栈展开操作已完成,程序继续执行。