文章目录
1.默认=运算符重载函数的局限
我们知道,当我们定义一个类A的时候,即使类体中我们什么都不写,C++编译器
也将自动为A 产生下面四个缺省函数
。
A(void); // 缺省的无参数构造函数
A(const A &a); // 缺省的拷贝构造函数
~A(void); // 缺省的析构函数
A & operate =(const A &a); // 缺省的赋值=函数
本文主要讲=运算符的重载,因此暂且不去管其它三个函数,重心放在赋值运算符重载函数上。刚接触C++的同学可能会疑惑:既然编译器能自动生成=运算符重载函数了,为什么还要我们自己再去写了。
但是对于含有指针
的类,编译器自动生成的=重载函数就满足不了我们的需求了,
class String
{
public:
private:
char *data; // 用于保存字符串
};
上面的String类没有自定义=运算符重载函数,当我们在程序中对String对象进行赋值操作时。
String a("Hello");
String b("World");
b = a; //赋值操作
使用的是编译器自动生成
的=重载函数,该函数作用是将数据成员按bit拷贝,最后的结果是导致最后a和b指向同一块字符串"Hello",而b原来指向的字符串"World"现在没有主人。这样会导致以下几个问题。
(1) b原来指向的字符串"World"成了无主之魂,没有机会释放,造成内存泄露。
(2) a,b现在指向同一字符串"Hello",可能会被释放2次,也会造成错误。
(3) a,b现在指向同一字符串"Hello",通过a修改字符串会影响b,反之亦然,会导致不确定的行为产生。
2.防止自我拷贝
不少同学可能知道大部分情况编译器自动生成的=重载函数不够用,需要自己编写,于是毫不犹豫的写出了下面的=重载函数。
String& String::operator=(const String &rhs)
{
delete[] data;
data = new char[strlen(rhs.data)+1];
strcpy(data, rhs.data);
return *this;
}
粗看之下好像确实没什么问题,其实我们可以考虑的更周全点,想想,如果代码中出现了下面的语句会出现什么情况呢?
String a("Hello");
a = a; //自我赋值
我们来分析下,赋值操作中,会先把自己的数据delete掉,现在rhs.data及*this.data都成了野指针
,后面的strlen、strcpy操作就会产生不确定的行为。关于野指针,前面文章C/C++指针使用常见的坑(请戳我)有总结。
知道了问题的根源就好办了,因此,完善的写法是在前面加上一段规避自我赋值的代码,很简单,就是先判断是不是自己赋值给自己,是的话就直接返回自己就OK了。
String& String::operator=(const String &rhs)
{
//防止自我赋值
if (this == &rhs)
{ return *this;
}
delete[] data;
data = new char[strlen(rhs.data)+1];
strcpy(data, rhs.data);
return *this;
}
学如逆水行舟,不进则退