copy-and-swap能提供强异常安全保证,因其将赋值拆为“构造独立临时对象”和“无异常交换”两阶段;需完备实现拷贝构造、析构、noexcept swap三成员,且operator=宜用值参并直接swap。

为什么 copy-and-swap 能提供强异常安全保证
因为整个赋值过程被拆成两个阶段:先用拷贝构造函数生成一个**完整、独立、可析构**的临时对象(这步失败,原对象状态完全不变);再用 swap 交换当前对象和临时对象的数据(这步必须 noexcept,否则强保证失效)。一旦中间抛异常,栈展开只销毁临时对象,原对象毫发无损。
关键点在于:强保证 = “全成功” 或 “全回滚”,而 copy-and-swap 把“修改本体”这个高危动作,替换成了“交换指针/句柄”这种轻量、不抛异常的操作。
实现时必须提供的三个成员
缺一不可,否则编译或运行会出问题:
-
copy constructor:用于构造传入参数的副本(注意:参数类型是const T&,不是T—— 那是旧写法;现代写法直接用值参,见下节) -
destructor:确保临时对象能干净析构(尤其要释放裸指针、关闭文件等) -
swap成员函数或友元非成员函数:必须声明为noexcept,且内部所有操作都不能抛异常(比如不能调用可能抛异常的std::swap对自定义类型,应手写按成员交换)
operator= 的现代写法:用值参代替 const 引用
C++11 后推荐直接把参数写成 T other,而不是 const T& other。这样能自动兼容拷贝和移动:
- 传左值 → 调用
copy constructor - 传右值 → 调用
move constructor(如果已定义),避免多余拷贝 - 函数体内直接
swap(*this, other)即可,无需手动判 self-assignment
示例:
MyClass& operator=(MyClass other) noexcept {
swap(*this, other);
return *this;
}
注意:这里 noexcept 是对整个函数的承诺,前提是 swap 确实不抛异常。
最容易踩的坑:swap 不满足 noexcept
很多初学者直接写 std::swap(*this, other),但 std::swap 对自定义类型默认调用其 operator= 和拷贝构造——这正好是你正在实现的东西,会造成无限递归或异常泄露。
正确做法是:提供专用的 swap 函数,并确保它只做底层交换(如交换指针、std::unique_ptr、std::string 等已保证 noexcept 的成员):
- 把
swap声明为friend void swap(MyClass& a, MyClass& b) noexcept - 函数体内用
using std::swap; swap(a.mPtr, b.mPtr);这种 ADL 方式,让标准类型走std::swap,自定义类型走它们自己的swap - 务必在函数末尾加
noexcept,否则operator=的noexcept声明无效
没加 noexcept 的 swap,会让整个 copy-and-swap 失去强异常安全意义——因为交换过程一旦抛异常,原对象就处于不确定状态。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











