05-4 运算符重载-赋值重载
在学习类中的构造函数和析构函数的时候,我们学习过:写了一个类,c++的编译器会给这个类自动添加四个函数。
- 默认构造函数(无参,函数体为空)
- 默认析构函数(无参,函数体为空)
- 默认拷贝构造函数(对属性进行值拷贝)
- 赋值运算符
operator=
,用于对属性进行值拷贝
在学习深拷贝和浅拷贝的时候我们也提到过:如果类中有属性是指向堆区的指针,那么则有可能出现深拷贝和浅拷贝的问题。
我们可以举个例子:
#include <iostream>
using namespace std;
ostream& operator<<(ostream& cout, class test& p);
class test
{
friend ostream& operator<<(ostream& cout, class test& p);
private:
int* p_m_A;
int m_B;
public:
test()
{
p_m_A = new int(10);
m_B = 20;
}
test(int A, int B)
{
p_m_A = new int(A);
m_B = B;
}
};
ostream& operator<<(ostream& cout, class test& p)
{
cout << "p_m_A = " << (int)p.p_m_A << " * p_m_A = " << *p.p_m_A << " m_B = " << p.m_B ;
return cout;
}
void test1()
{
class test t1;
cout << "t1" << t1 << endl;
class test t2(20, 30);
cout << "t2" << t2 << endl;
t2 = t1;
cout << "t2" << t2 << endl;
}
int main()
{
test1();
return 0;
}
运行结果:(环境:Windows11(arm/Apple M VM)/Visual Studio 2022/Debug/arm64)
t1p_m_A = 446792128 * p_m_A = 10 m_B = 20
t2p_m_A = 446792256 * p_m_A = 20 m_B = 30
t2p_m_A = 446792128 * p_m_A = 10 m_B = 20
在这个类test
中,有两个属性,一个属性是p_m_A
(这是一个指针),另外一个是m_B
。
我们先创建t1
,然后将t1
使用赋值的方式赋值给t2
,因为默认的赋值是值拷贝,会把t1.p_m_A
和t1.m_B
的数据值一模一样赋值给t2
。这时候,t1
和t2
两个对象中的p_m_A
的数值完全一样。但是,因为p_m_A
是一个地址,所以访问的实际数据都是同一个,意味着t1
和t2
无论哪个对象被删除都会造成非法访问。
如果我们加上一段析构函数:
~test()
{
if (p_m_A != NULL)
{
delete p_m_A;
p_m_A = NULL;
}
}
再次运行程序,程序会当场崩溃。
这是因为,我们创建t1
的时候,数据会在堆区开辟一块内存用于存放数据,p_m_A
会记录存放内存数据的地址。当我们使用默认提供的赋值时,因为是值拷贝,会完完全全的拷贝t1.p_m_A
上的数据(此时,这上面的数据是地址)到t2.p_m_A
。
当析构代码被执行的时候,t2
被释放了,析构函数中会检测t2.p_m_A
是否为空。因为不为空,所以会释放一次。之后再次释放t1
的时候,再次检查t1.p_m_A
是否为空,则会再次释放这块内存。导致一个内存被两次释放。
解决方法:使用赋值运算符重载,进行深拷贝。将类中出现的地址使用new
操作符新申请一块内存空间存放。
class test& operator=(class test& p)
{
if (p_m_A != NULL)
{
delete p_m_A;
p_m_A = NULL;
}
p_m_A = new int(*p.p_m_A);
m_B = p.m_B;
return *this;
}
请注意,赋值运算符重载只可以在成员函数中重载。
- 思路:
- 检测被赋值的对象中是否有指向堆区的数据,如果有,需要删除。
- 将需要赋值的原数据中指针的变量先解引用,然后使用
new
创建出一块新的内存存储 - 为了实现链式编程,返回值需要返回自己。在类中的成员函数返回自己需要使用
*this
。
- 注意:
- 如果类中有属性是指向堆区的指针,那么则有可能出现深拷贝和浅拷贝的问题。
- 赋值运算符重载只可以在成员函数中重载。
- 如果类中没有指向堆区的地址,可以使用编译器提供的默认赋值。(默认提供值拷贝)。
:-)