MST

星途 面试题库

面试题:C++ 析构函数异常与栈展开

考虑如下 C++ 代码片段: ```cpp class A { public: ~A() { throw std::runtime_error("A's destructor exception"); } }; class B { A a; public: ~B() { // some code here } }; void func() { B b; } ``` 当 `func` 函数执行时,描述异常抛出后栈展开的过程以及可能导致的程序行为,同时说明如何修改代码来避免潜在问题。
19.5万 热度难度
编程语言C++

知识考点

AI 面试

面试题答案

一键面试

栈展开过程及程序行为

  1. func 函数执行func 函数创建了一个 B 类型的对象 b。当 func 函数结束时,b 的析构函数会被调用。
  2. B 的析构函数执行:在 B 的析构函数中,由于 B 包含一个 A 类型的成员变量 a,所以 a 的析构函数会首先被调用。
  3. A 的析构函数抛出异常A 的析构函数抛出了一个 std::runtime_error 异常。此时栈开始展开,B 的析构函数尚未完成执行,因为 A 析构函数的异常导致控制权转移。
  4. 异常处理:由于 func 函数中没有捕获该异常,异常会继续向上层调用栈传播。如果一直到 main 函数都没有捕获该异常,根据 C++ 标准,程序将调用 std::terminate 函数,通常这会导致程序异常终止。

修改代码避免潜在问题的方法

  1. 捕获异常:在 B 的析构函数中捕获 A 析构函数抛出的异常,避免异常传播出 B 的析构函数。
class B {
    A a;
public:
    ~B() {
        try {
            // 这里可能已经有其他代码
        } catch (...) {
            // 处理异常,例如记录日志
        }
    }
};
  1. 资源管理方式:使用智能指针来管理资源,这样在对象析构时,资源的释放可以由智能指针更安全地处理。例如,如果 A 管理的是动态分配的资源,可以用 std::unique_ptr 代替 A 成员变量。
class B {
    std::unique_ptr<A> a;
public:
    B() : a(std::make_unique<A>()) {}
    ~B() {
        // 智能指针会自动处理 A 的析构,异常处理更安全
    }
};

通过这些修改,可以避免异常从析构函数传播导致 std::terminate 被调用,使程序的异常处理更加稳健。