C++中已有拷贝构造函数为什么还需要实现赋值运算符?
关于C++三法则与赋值运算符实现的解答
为什么只实现拷贝构造函数不足以让类正常运行
首先要区分拷贝构造和拷贝赋值的触发场景:
- 拷贝构造是在创建新对象时,用另一个已有对象初始化触发,比如
dumb_array a = b;、dumb_array a(b); - 拷贝赋值运算符
operator=是在两个已存在的对象之间做赋值操作时触发,比如你已经定义了dumb_array a(10); dumb_array b(5);,再执行a = b;就会调用operator=
你给出的代码里没有自定义operator=,编译器会自动生成默认的拷贝赋值运算符,默认实现是对所有成员做浅拷贝,会直接导致两个严重问题:
- 内存泄漏:赋值目标对象原来持有的堆内存没有被释放,指针被直接覆盖,对应的内存永远无法回收
- 重复释放:赋值完成后两个对象的指针会指向同一块堆内存,两个对象析构时会对同一块内存执行两次
delete[],触发未定义行为,大概率直接崩溃
比如下面的测试代码就会触发问题:
int main() { dumb_array arr1(10); dumb_array arr2(5); arr2 = arr1; // 调用默认operator=,直接触发异常 return 0; }
什么是三法则(Rule of Three)
三法则是C++的经典编码约定:如果你的类需要自定义析构函数、拷贝构造函数、拷贝赋值运算符三者中的任意一个,那么三者都需要自定义。
这个规则的核心逻辑是:需要自定义这三个函数的任意一个,就说明类需要自行管理默认语法无法自动管理的资源(比如堆内存、文件句柄、网络连接等),默认的浅拷贝语义无法满足资源管理的需求,三个函数的资源管理逻辑是强相关的,必须配套实现。
正确的operator=实现参考
推荐用复制交换(copy-and-swap)的写法,既安全又可以复用已有的拷贝构造逻辑:
首先给类添加友元swap函数:
friend void swap(dumb_array& first, dumb_array& second) noexcept { // 交换所有成员 std::swap(first.mSize, second.mSize); std::swap(first.mArray, second.mArray); }
然后实现operator=:
dumb_array& operator=(dumb_array other) { // 按值传参,自动调用拷贝构造生成临时对象 swap(*this, other); // 交换本对象和临时拷贝的内容 return *this; } // 临时对象other出作用域自动析构,原来的旧资源会被自动释放
这种写法天然避免了自赋值问题,同时异常安全,如果拷贝构造抛出异常,本对象的内容不会被修改。
内容的提问来源于stack exchange,提问作者Sachin Palande
相关产品推荐
相关产品推荐

