17370845950

c++如何写出异常安全的代码 c++异常安全三原则【规范】
异常安全的核心目标是确保异常发生时程序状态有效:不泄漏资源、不损坏数据、不破坏不变量;C++提供基本、强和不抛异常三类保证,需依托RAII、Copy-and-Swap及noexcept移动/析构实现。

异常安全的核心目标

异常安全不是“不抛异常”,而是确保程序在异常发生时仍保持有效状态:对象不泄漏、数据不损坏、不变量不被破坏。C++中主要关注三类保证:基本异常安全(资源不泄漏,对象处于可析构状态)、强异常安全(操作要么完全成功,要么回滚到调用前状态)、不抛异常安全(noexcept,关键路径如析构函数、移动操作必须满足)。

坚持RAII:资源管理的基石

所有资源(内存、文件、锁、socket等)必须封装在栈对象中,由构造函数获取、析构函数释放。避免裸指针和手动delete/new。

  • std::unique_ptr替代new/delete,转移所有权自动释放
  • std::shared_ptr管理共享资源,引用计数保障生命周期
  • 互斥锁用std::lock_guardstd::scoped_lock,作用域结束即解锁
  • 文件句柄可用std::fstream,RAII自动关闭

编写强异常安全的函数:Copy-and-Swap惯用法

赋值运算符、容器插入等易出错操作,应先完成所有可能抛异常的操作(如内存分配、拷贝),再原子切换状态。

  • 实现swap()为noexcept成员函数(通常只交换指针/整数)
  • 赋值运算符按“拷贝构造临时对象 → swap → 临时对象析构”流程写
  • 例如:T& operator=(T other) { swap(*this, other); return *this; }(注意参数传值,触发拷贝)

析构函数与移动操作必须noexcept

析构函数若抛异常,可能导致std::terminate;移动构造/赋值若未声明noexcept,容器(如std::vector扩容)将退化为拷贝而非移动。

  • 所有析构函数默认隐式noexcept,切勿在其中调用可能抛异常的函数
  • 移动操作显式加noexceptT(T&&) noexcept { ... }
  • 自定义swap也应声明为noexcept,供标准库内部调用