12.5.4 赋值运算符重载
我们再创建一个类对象后,有时会有这样的操作。
Person p1("张三", 18);
person p2;
p2 = p1;
注意看第三行代码,使用了赋值运算符,按理说没有重载前赋值运算符应该不能操作自定义数据类型。
这是因为在一个类定义完成后,编译器会至少生成四种函数:
- 默认构造函数
- 默认析构函数
- 默认拷贝构造函数
- 赋值运算符重载函数
但是使用默认拷贝构造函数和默认赋值运算符重载函数时要注意,编译器自动生成的都是浅拷贝。如果有属性是定义在堆区的指针,就要避免入坑。
我们可以手动重载赋值运算符,将浅拷贝变为深拷贝。
1、首先我们创建一个Person类,为了简单,只定义一个m_Age属性
class person
{
public:
int *m_Age;
};
2、再在类中加上构造函数
由于年龄属性是一个指针变量,构造函数应用new关键字在堆区返回一个指针。
person(int age)
{
this->age = new int(age);
}
3、堆区的数据应由程序员手动开辟和释放,所以要加上析构函数释放属性占用的堆区空间。
~person()
{
if (this->age != NULL)
{
delete age;
age = NULL;
}
}
4、对于堆区的数据,如果使用浅拷贝,会出现释放内存出错的bug,这里之前介绍深浅拷贝时已经详细解释过。所以我们要重载拷贝构造函数和赋值运算符。为了方便,本节只重载赋值运算符。
在类中添加这样的函数:
person& operator=(person &p)
{
//编译器提供的赋值运算符重载,是浅拷贝
//this->age = p.age;
//深拷贝
//首先要先将本来的指针指向的内存释放干净,再指向新的内存
if (this->age != NULL)
{
delete age;
age = NULL;
}
this->age = new int(*p.age);
return *this;
}
- 这里返回Person&是为了实现连等的使用。如p1=p2=p3;
完成上面的工作之后,我们就完成了赋值运算符的重载,并且是深拷贝。
用一个示例区验证以下代码:
void test1()
{
person p1(10);
person p2(20);
person p3(30);
p3 = p2 = p1;
cout << *p1.m_Age << endl;
cout << *p2.m_Age << endl;
cout << *p3.m_Age << endl;
}
int main()
{
test1();
system("pause");
return 0;
}
运行结果正常,且没有出现内存释放出错的问题: