1. 赋值运算符重载函数 的参数是函数所在类的const类型的引用
MyStr& operator =(const MyStr& str)//赋值运算符
{
cout << "operator =" << endl;
if (this != &str)
{
if (name != NULL)
delete[] name;
this->id = str.id;
int len = strlen(str.name);
name = new char[len + 1];
strcpy_s(name, strlen(str.name) + 1, str.name);
}
return *this;
}
(1)加const是因为:
- 我们不希望在这个函数中对用来进行赋值的“原版”做任何修改。
- 加上const,对于const的和非const的实参,函数就能接受;如果不加,就只能接受非 const的实参。
(2)用引用是因为:
这样可以避免在函数调用时对实参的一次拷贝,提高了效率。
(3)对于返回值:
一般地,返回值是被赋值者的引用,即*this,原因是
- 这样在函数返回时避免一次拷贝,提高了效率。
- 更重要的,这样可以实现连续赋值,即类似a=b=c这样。如果不是返回引用而是返回值类型,那么,执行a=b时,调用赋值运算符重载函数,在函数返回时,由于返回的是值类型,所以要对return后边的“东西”进行一次拷贝,得到一个未命名的副本(有些资料上称之为“匿名对象”),然后将这个副本返回,而这个副本是右值,所以,执行a=b后,得到的是一个右值,再执行=c就会出错。
(4)赋值运算符重载函数不能被继承。
(5)赋值运算符重载函数要避免自赋值
对于赋值运算符重载函数,我们要避免自赋值情况(即自己给自己赋值)的发生,一般地,我们通过比较赋值者与被赋值者的地址是否相同来判断两者是否是同一对象(正如例1中的if (this != &str)一句)。
为什么要避免自赋值呢?
- 为了效率。显然,自己给自己赋值完全是毫无意义的无用功,特别地,对于基类数据成员间的赋值,还会调用基类的赋值运算符重载函数,开销是很大的。如果我们一旦判定是自赋值,就立即return *this,会避免对其它函数的调用。
- 如果类的数据成员中含有指针,自赋值有时会导致灾难性的后果。对于指针间的赋值(注意这里指的是指针所指内容间的赋值,这里假设用_p给p赋值),先要将p所指向的空间delete掉(为什么要这么做呢?因为指针p所指的空间通常是new来的,如果在为p重新分配空间前没有将p原来的空间delete掉,会造成内存泄露),然后再为p重新分配空间,将_p所指的内容拷贝到p所指的空间。如果是自赋值,那么p和_p是同一指针,在赋值操作前对p的delete操作,将导致p所指的数据同时被销毁。那么重新赋值时,拿什么来赋?
所以,对于赋值运算符重载函数,一定要先检查是否是自赋值,如果是,直接return *this。