编程C++后端开发者

C++中的异常系统是如何工作的,与其他语言有什么不同?在工业应用中如何正确设计异常处理?

用 Hintsage AI 助手通过面试

答案

C++中的异常系统基于try-catch机制和关键字throw。当发生异常情况(例如,资源错误、机制违反)时,在try块内通过throw操作符抛出异常。查找合适的处理程序(catch)的操作是通过展示堆栈直到第一个匹配类型。

重要细节:

  • 与Java或C#不同,C++中的异常可以是任何类型(通常是派生自std::exception的类)。
  • C++中没有强制的异常规范(如Java中的检查异常),而是从C++11起引入了noexcept说明符。
  • 抛出异常时,会调用在抛出点和处理点之间堆栈上所有对象的析构函数 — 重要的是支持RAII。

设计:

  • 仅抛出“意外”错误的异常,对其他情况使用返回代码。
  • 通过常量引用捕捉 — catch(const std::exception& e)
  • GNU建议抛出可复制对象而不是指针(避免堆内存)。

代码示例:

#include <iostream> #include <stdexcept> void mayFail(bool fail) { if (fail) throw std::runtime_error("进程错误"); } int main() { try { mayFail(true); } catch (const std::exception& ex) { std::cout << "捕获到异常: " << ex.what() << std::endl; } }

陷阱问题

如果异常在处理另一个异常时从析构函数中抛出,会发生什么?

答案: 将导致程序通过调用std::terminate()异常终止,因为在处理异常时,不允许生成新的异常(双重异常),否则会破坏堆栈展开。

示例:

struct CrashOnDestruct { ~CrashOnDestruct() noexcept(false) { throw std::runtime_error("析构函数中的错误!"); } }; void func() { CrashOnDestruct obj; throw std::logic_error("错误处理"); } int main() { try { func(); } catch (...) { } } // 将通过std::terminate()终止

由于对主题细节的不熟悉而导致的实际错误示例


故事
在一个大型金融应用程序中,异常是从数据库包装类的析构函数中抛出的。一个主要事务在出现错误时抛出异常,在展开堆栈时调用析构函数,也抛出错误。整个应用程序崩溃,用户工作丢失。开发人员不得不紧急将析构函数中的throw替换为记录日志和正确处理。


故事
一个微服务处理类型为:catch(std::exception)的异常。一个线程抛出了用户自定义类型的异常(未从std::exception继承)。这些错误未被捕获,导致连接意外中断和内存泄漏。


故事
缺少移动容器方法中的noexcept说明符导致标准库使用慢拷贝而不是快速移动操作;性能的显著下降只有在负载测试中被发现。