默认赋值运算符在类含指针或动态资源时导致浅拷贝,引发 double free 或 use-after-free;须手动实现深拷贝,推荐拷贝-交换惯用法,并遵循三法则或 rule of zero。

为什么不能直接用默认赋值运算符
当类里有指针成员或动态分配的资源时,operator= 默认行为是浅拷贝——两个对象指向同一块内存。后续一个对象析构时释放资源,另一个再访问就会崩,常见报错如 double free 或 use-after-free。
典型场景:自定义字符串类、容器类、封装了 new / malloc 的类。
- 必须手动实现深拷贝逻辑(比如对指针成员
new一块新内存并复制内容) - 要检查自赋值:
if (this == &other) return *this;,否则清空自己再拷贝自己就丢数据 - 返回类型必须是
T&(通常是MyClass&),支持链式赋值如a = b = c;
operator= 的标准写法模板
推荐使用“拷贝-交换”(copy-and-swap)惯用法,天然规避自赋值问题,且异常安全:
MyClass& MyClass::operator=(MyClass other) { // 注意:参数按值传入,触发拷贝构造
swap(*this, other); // 交换 this 和 other 的内部资源
return *this;
}
前提是必须提供无异常的 swap 函数(通常为友元或成员),且类有正确的拷贝构造函数。
- 参数不加
const&是故意的——靠拷贝构造来生成临时副本 - 如果拷贝构造本身可能抛异常,那这个
operator=也会抛,但至少不会破坏原对象状态 - 比起传统写法(先清理、再分配、再复制),它更简洁、更难出错
容易漏掉的三个细节
很多人写了 operator= 却还是出问题,往往栽在这几个地方:
- 没声明为
public:如果类里其他函数要赋值,而operator=是private或没写,编译器会报no match for operator= - 忘了禁用移动赋值(C++11+):如果只重载了拷贝赋值,但没显式删除或定义移动赋值,编译器可能生成默认的
operator=(MyClass&&),行为仍是浅拷贝 - 异常安全性被忽略:传统写法中,若
new失败抛出std::bad_alloc,对象可能已部分修改(比如旧内存已被delete,新内存又没分配成),变成半残状态
要不要同时重载拷贝构造和析构函数
只要重载了 operator=,基本意味着类管理了外部资源——这时候三法则(Rule of Three)大概率适用:你很可能也需要自定义拷贝构造函数和析构函数。
如果只重载 operator= 而留着默认拷贝构造,那拷贝构造出来的对象和赋值后的对象行为不一致,极易埋雷。
- 检查类里有没有
new、fopen、pthread_mutex_init这类资源申请操作 - 如果有,三者(析构、拷贝构造、赋值)最好一起实现,或者直接用 Rule of Zero:把资源封装进 RAII 类型(如
std::unique_ptr),让编译器生成的版本就足够安全 - 现代 C++ 中,优先考虑用
std::vector、std::string、std::unique_ptr替代裸指针,能省掉 90% 的手工operator=实现
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











