C++深度解析 经典问题解析三 --- 赋值操作符重载 和 拷贝构造函数,string类的疑问(35)
赋值操作符
什么时候需要重载赋值操作符?
编译器是否提供默认的赋值操作?
关于赋值的疑问:
编译器为每个类默认重载了赋值操作符。
默认的赋值操作符仅完成浅拷贝。
当需要进行深拷贝时必须重载赋值操作符。
赋值操作符与拷贝构造函数有相同的存在意义。
示例程序:(默认赋值操作符重载)
#include <iostream>
#include <string>
using namespace std;
class Test
{
int *m_pointer;
public:
Test()
{
m_pointer = NULL;
}
Test(int i)
{
//将指针指向堆空间一个int类型的数据(4个字节)
m_pointer = new int(i);
}
//拷贝构造函数,深拷贝
Test(const Test& obj)
{
//向堆空间申请一片内存,取出参数对象的pointer指针所指向的值,赋值新申请的内存空间
m_pointer = new int(*obj.m_pointer);
}
//赋值操作符的重载,返回值类型是引用,为了连续赋值
Test& operator = (const Test& obj)
{
//不能自己赋值给自己
if (this != &obj)
{
delete m_pointer;
//深拷贝
m_pointer = new int(*obj.m_pointer);
}
return *this;
}
void print()
{
//将m_pointer以十六进制输出
cout << "m_pointer = " << hex << m_pointer << endl;
}
~Test()
{
delete m_pointer;
}
};
int main()
{
//t1类部成员指针将指向堆空间里面的一块内存,并且将内存的值设置为1
Test t1 = 1;
//t2类部的指针指向空
Test t2;
t2 = t1;
t1.print();
t2.print();
return 0;
}
结果如下:
分析:t1和t2所指向的堆空间是不一样,因为代码t2 = t1赋值操作,调用重载赋值操作符函数。赋值操作符和拷贝构造函数具有同等的意义。如果要进行深拷贝,必须提供赋值操作符的重载实现。但凡进行深拷贝,拷贝构造函数和赋值操作符实现都要自定义。
注意:赋值操作符重载函数,需要遵循下面四点:
- 返回值类型是一个引用,为了连续赋值。
- 参数是const引用类型。
- 赋值操作,不能自己赋值给自己,当前this对象和参数对象的地址不同。
- 返回当前对象。
IntArray& operator = (const IntArray& obj)
{
if (this != &obj)
{
}
return *this;
}
编译器默认提供的函数
class Test
{
//里面是由东西的!!不是空的
};
等价于:
class Test
{
public:
Test(); //无参数构造函数
Test(const Test&); //拷贝构造函数
Test& operator=(const Test&); //赋值操作符重载
~Test(); //析构函数
};
分析:编译器会为空的类,提供四个函数的实现:无参数构造函数,拷贝构造函数,赋值操作符重载,析构函数。
关于string的疑问
示例程序一:
#include <iostream>
#include <string>
using namespace std;
int main()
{
string s = "12345";
//c_str():返回字符指针,返回一个C语言方式的字符串,混合了C和C++的编程方式
const char* p = s.c_str();
cout << p << endl;
//重新申请一块堆空间
s.append("abcde"); // p 成为了野指针
cout << p << endl;
return 0;
}
结果如下:
分析:string的内部有一个字符指针m_cstr。在代码s.append("abced")中,将重新申请一片堆空间,并且把原来的12345和现在的abced一起赋值到新的堆空间里面。不要混合C和C++编程方式。
示例程序二:
#include <iostream>
#include <string>
using namespace std;
int main()
{
const char* p = "12345";
string s = "";
//将字符串对象内部的数据指针所指向的堆空间大小变为10个字节
s.reserve(10);
//不要使用 C语言中的方式操作 C++中的字符串
for(int i = 0; i < 5; i++)
{
s[i] = p[i];
}
cout << s << endl;
return 0;
}
结果如下:
分析:string类内部的数据指针m_cstr指向堆空间内存,还有一个表示长度的成员变量m_length。数据指针m_cstr指向堆空间的内容发现变化,但是字符串长度的成员变量m_length还是0。
示例程序三:
#include <iostream>
#include <string>
using namespace std;
int main()
{
const char* p = "12345";
string s = "";
s = p;
cout << s << endl;
return 0;
}
小结
在需要进行深拷贝的时候必须重载赋值操作符。
赋值操作符和拷贝构造函数有同等重要的意义。
string类通过一个数据空间保存字符数据。
string类通过一个成员变量保存当前爱你字符串的长度。
C++开发时尽量避开C语言中惯用的编程思想。