面试题答案
一键面试栈展开过程及程序行为
func
函数执行:func
函数创建了一个B
类型的对象b
。当func
函数结束时,b
的析构函数会被调用。B
的析构函数执行:在B
的析构函数中,由于B
包含一个A
类型的成员变量a
,所以a
的析构函数会首先被调用。A
的析构函数抛出异常:A
的析构函数抛出了一个std::runtime_error
异常。此时栈开始展开,B
的析构函数尚未完成执行,因为A
析构函数的异常导致控制权转移。- 异常处理:由于
func
函数中没有捕获该异常,异常会继续向上层调用栈传播。如果一直到main
函数都没有捕获该异常,根据 C++ 标准,程序将调用std::terminate
函数,通常这会导致程序异常终止。
修改代码避免潜在问题的方法
- 捕获异常:在
B
的析构函数中捕获A
析构函数抛出的异常,避免异常传播出B
的析构函数。
class B {
A a;
public:
~B() {
try {
// 这里可能已经有其他代码
} catch (...) {
// 处理异常,例如记录日志
}
}
};
- 资源管理方式:使用智能指针来管理资源,这样在对象析构时,资源的释放可以由智能指针更安全地处理。例如,如果
A
管理的是动态分配的资源,可以用std::unique_ptr
代替A
成员变量。
class B {
std::unique_ptr<A> a;
public:
B() : a(std::make_unique<A>()) {}
~B() {
// 智能指针会自动处理 A 的析构,异常处理更安全
}
};
通过这些修改,可以避免异常从析构函数传播导致 std::terminate
被调用,使程序的异常处理更加稳健。