我们现在有一个问题:什么时候需要重载赋值运算符?
下面我们进行编程实验看一下深拷贝的意义:
#include
#include
using namespace std;
class Test
{
int* m_pointer;
public:
Test()
{
m_pointer = NULL;
}
Test(int i)
{
m_pointer = new int(i);
}
void print()
{
cout << "m_pointer = " << hex << m_pointer << endl;
}
~Test()
{
delete m_pointer;
}
};
int main()
{
Test t1 = 1;
Test t2;
t2 = t1;
t1.print();
t2.print();
return 0;
}
上面的代码很简单,t2 = t1 时,使用编译器默认的重载赋值运算符,这里是浅拷贝,t1 和 t2 中的 m_pointer 都指向同一块内存空间,t1,t2 生存期结束时,调用析构函数,释放两次空间,会引起程序崩溃。
如何修改呢,就是自己实现拷贝构造函数和赋值运算符,实现深拷贝。
#include
#include
using namespace std;
class Test
{
int* m_pointer;
public:
Test()
{
m_pointer = NULL;
}
Test(int i)
{
m_pointer = new int(i);
}
Test(const Test& obj)
{
m_pointer = new int(*obj.m_pointer);
}
Test& operator = (const Test& obj) // 返回值为类型引用,参数为const类型的引用
{
if (this != &obj) // 相等判断,避免自我赋值
{
delete m_pointer;
m_pointer = new int(*obj.m_pointer);
}
return *this; // 返回 *this
}
void print()
{
cout << "m_pointer = " << hex << m_pointer << endl;
}
~Test()
{
delete m_pointer;
}
};
int main()
{
Test t1 = 1;
Test t2;
t2 = t1;
t1.print();
t2.print();
return 0;
}
使用深拷贝,二者的地址空间不同,两次析构释放的是不同的地址空间
$ g++ 24-1.cpp -o 24-1
$ ./24-1
m_pointer = 0x90db008
m_pointer = 0x90db018
赋值操作符与拷贝构造函数有相同的存在意义,使用场合不同,t2 = t1 使用赋值操作符,t2(t1) 使用拷贝构造函数。意义是相同的。
重载赋值操作符需要注意四点(非常重要):
在博客【C++深度解析】12、构造函数、拷贝构造函数和析构函数中我们实现了数组类
在【C++深度解析】17、二阶构造模式中使用二阶构造模式进行改进,避免构造失败而形成半成品对象。
在【C++深度解析】22、数组操作符重载中重载数组操作符,这样可以直接对对象使用下标访问数组元素。
数组类中有指针会申请内存空间,拷贝时发生的是浅拷贝,容易造成内存错误,这里我们重载赋值操作符,实现深拷贝。另外这里的拷贝构造函数为私有,类外无法调用,不需要重载了。
// IntArray.h
#ifndef _INTARRAY_H_
#define _INRARRAY_H_
class IntArray
{
private:
IntArray(int len);
bool construct();
IntArray(const IntArray& obj);
int m_length;
int* m_pointer;
public:
static IntArray* NewInstance(int length);
int length();
bool get(int index, int& value);
bool set(int index, int value);
int& operator [] (int index);
IntArray& operator = (const IntArray& obj);
IntArray& self();
~IntArray();
};
#endif
#include"IntArray.h"
#include
IntArray::IntArray(int len)
{
m_length = len;
}
bool IntArray::construct()
{
bool ret = true;
m_pointer = new int[m_length];
if (m_pointer)
{
for (int i = 0; i < m_length; i++)
{
m_pointer[i] = 0;
}
}
else
{
ret = false;
}
return ret;
}
IntArray* IntArray::NewInstance(int length)
{
IntArray* ret = new IntArray(length);
if (!(ret && ret->construct()))
{
delete ret;
ret = NULL;
}
return ret;
}
int IntArray::length()
{
return m_length;
}
bool IntArray::get(int index, int& value)
{
bool ret = (index >= 0 && index < m_length);
if (ret)
{
value = m_pointer[index];
}
return ret;
}
bool IntArray::set(int index, int value)
{
bool ret = (index >= 0 && index < m_length);
if (ret)
{
m_pointer[index] = value;
}
return ret;
}
int& IntArray::operator [] (int index)
{
return m_pointer[index];
}
IntArray& IntArray::operator = (const IntArray& obj)
{
if (this != &obj)
{
int* pointer = new int[obj.m_length];
if (pointer)
{
for (int i = 0; i < obj.m_length; i++)
{
pointer[i] = obj.m_pointer[i];
}
m_length = obj.m_length;
delete[] m_pointer;
m_pointer = pointer;
}
}
return *this;
}
IntArray& IntArray::self()
{
return *this;
}
IntArray::~IntArray()
{
delete[]m_pointer;
}
// 24-2.cpp
#include
#include"IntArray.h"
using namespace std;
int main()
{
IntArray* a = IntArray::NewInstance(5);
IntArray* b = IntArray::NewInstance(10);
if (a && b)
{
IntArray& array = a->self();
IntArray& brray = b->self();
cout << "array.length = " << array.length() << endl;
cout << "brray.length = " << brray.length() << endl;
array = brray;
cout << "array.length = " << array.length() << endl;
cout << "brray.length = " << brray.length() << endl;
}
delete a;
delete b;
return 0;
}
重载赋值运算符实现深拷贝,这样对象使用赋值符号赋值就不会引起内存错误了。因为 不同对象指向的是不同的地址空间。
$ g++ 24-2.cpp IntArray.cpp -o 24-2
$ ./24-2
array.length = 5
brray.length = 10
array.length = 10
brray.length = 10
1、需要深拷贝时必须重载赋值操作符
2、赋值操作符和拷贝构造函数有同等重要的意义