从浅拷贝到深拷贝:C++赋值运算符重载的核心技术
目录
一、赋值运算符重载的核心特性
1、成员函数重载
2、编译器默认生成
3、实现注意事项
二、何时需要自定义赋值运算符重载
重要原则
三、代码示例讲解
拷贝构造与赋值运算符的区别
关键要点
1. 参数类型设置为引用并用const修饰
2. 返回值使用引用
3. 自赋值检查
4. 编译器生成的默认赋值运算符
四、何时需要自定义赋值运算符
赋值运算符重载是C++中一个重要的默认成员函数,用于在两个已经存在的对象之间进行拷贝赋值。它与拷贝构造函数有本质区别:拷贝构造函数是用一个已存在的对象来初始化一个新创建的对象,而赋值运算符重载则是在两个已存在的对象之间进行赋值操作。
一、赋值运算符重载的核心特性
1、成员函数重载
-
赋值运算符必须重载为类的成员函数
-
参数建议使用
const 类类型&
,避免不必要的拷贝构造 -
返回类型建议使用
类类型&
,支持连续赋值并提高效率
2、编译器默认生成
-
若未显式实现,编译器会自动生成默认赋值运算符重载
-
默认实现行为:
-
对内置类型成员:值拷贝/浅拷贝(逐字节复制)
-
对自定义类型成员:调用其默认生成的赋值运算符重载
-
3、实现注意事项
-
需要检查自赋值情况(
if(this != &d)
) -
应返回
*this
以支持连续赋值 -
返回值类型为引用以减少拷贝开销
二、何时需要自定义赋值运算符重载
类类型 | 是否需要自定义 | 原因 |
---|---|---|
Date 类(全内置类型) | 否 | 编译器生成的浅拷贝已足够 |
Stack 类(管理资源) | 是 | 需要实现深拷贝 |
MyQueue 类(含自定义类型成员) | 否 | 自动调用成员的赋值运算符 |
重要原则
如果一个类需要显式实现析构函数来释放资源,那么它通常也需要显式实现赋值运算符重载。
三、代码示例讲解
class Date {
public:Date(int year = 1, int month = 1, int day = 1) : _year(year), _month(month), _day(day) {}// 拷贝构造函数Date(const Date& d) {cout << "Date(const Date& d)" << endl;_year = d._year;_month = d._month;_day = d._day;}// 赋值运算符重载Date& operator=(const Date& d) {// 检查自赋值if (this != &d) {_year = d._year;_month = d._month;_day = d._day;}return *this; // 支持连续赋值}void Print() {cout << _year << "-" << _month << "-" << _day << endl;}private:int _year;int _month;int _day;
};int main() {Date d1(2024, 7, 5); // 构造函数Date d2(d1); // 拷贝构造函数Date d3(2024, 7, 6); d1 = d3; // 赋值运算符重载// 注意:这是拷贝构造,不是赋值重载Date d4 = d1; return 0;
}
拷贝构造与赋值运算符的区别
Date d1(2024, 1, 1); // 构造函数
Date d2 = d1; // 拷贝构造函数(创建新对象)
Date d3;
d3 = d1; // 赋值运算符重载(两个已存在对象)
-
拷贝构造函数:用一个已存在的对象初始化一个新创建的对象
-
赋值运算符:在两个已存在的对象之间进行赋值操作
关键要点
1. 参数类型设置为引用并用const修饰
-
赋值运算符重载函数的第一个形参是隐式的
this
指针,第二个形参是赋值运算符的右操作数 -
使用引用传参避免不必要的拷贝构造调用
-
添加
const
修饰确保不会修改右操作数,同时保持常性,不会造成权限放大
2. 返回值使用引用
-
为了支持连续赋值(如
d3 = d2 = d1
),需要返回赋值后的左操作数 -
返回引用避免不必要的拷贝
-
返回
*this
因为this
指针指向的就是左操作数
3. 自赋值检查
-
在赋值前检查是否是自赋值(如
d1 = d1
) -
避免不必要的操作,同时也是防御性编程的重要部分
4. 编译器生成的默认赋值运算符
-
如果类没有显式定义赋值运算符重载,编译器会自动生成一个
-
默认实现是按字节序的值拷贝(浅拷贝)
-
对于简单类(如日期类)通常足够,但对于管理资源的类需要自定义
四、何时需要自定义赋值运算符
当类满足以下条件之一时,通常需要自定义赋值运算符:
-
类包含指针成员并管理动态内存
-
类需要深拷贝语义
-
类有需要特殊处理的资源(如文件句柄、网络连接等)
-
类有需要保持唯一性的成员(如ID)
对于简单的值类(如日期类),编译器生成的默认赋值运算符通常就足够了。
理解赋值运算符重载与拷贝构造函数的区别对掌握C++对象模型至关重要。对于管理资源的类,正确的实现赋值运算符重载可以避免内存泄漏和重复释放等问题。