一、提供默认赋值运算符重载函数的时机
当程序没有显示地提供一个以本类或者本类的引用为参数的赋值运算符重载函数时,编译器会自动生成这样一个赋值运算符重载函数。
#include <iostream>
using namespace std;
class Data {
public:
Data() {}
Data(int _data) :data(_data)
{
cout << "constructor" << endl;
}
Data& operator = (const int _data)
{
cout << "operator = (const int _data)" << endl;
data = _data;
return *this;
}
private:
int data;
};
void main()
{
Data data1(1);
Data data2, data3;
data2 = 1;
data3 = data2;//调用编译器提供的默认的赋值运算符重载函数
return;
}
运行结果:
二、浅拷贝和深拷贝
这是拷贝构造函数和赋值运算符重载函数都会涉及到的这个问题。
所谓浅拷贝,就是说编译器提供的默认的拷贝构造函数和赋值运算符重载函数,仅仅是将对象中各个数据成员的值拷贝给另一个同一个类对象对应的数据成员。
在深拷贝情况下,对于对象中动态成员,就不能仅仅简单地赋值,而应该重新动态分配空间。
#include <iostream>
#include <string>
using namespace std;
class MyStr {
public:
MyStr() {}
MyStr(int _id, char *_name)
{
cout << "constructor" << endl;
id = _id;
name = new char[strlen(_name) + 1];
strcpy_s(name,strlen(_name) + 1,_name);
}
MyStr(const MyStr &str)
{
cout << "copy constructor" << endl;
id = str.id;
if (name != NULL)
delete name;
name = new char[strlen(str.name) + 1];
strcpy_s(name,strlen(str.name) + 1,str.name);
}
MyStr& operator=(const MyStr& str)
{
cout << "operator=" << endl;
if (this != &str)
{
if (name != NULL)
delete name;
this->id = str.id;
name = new char[strlen(str.name) + 1];
strcpy_s(name,strlen(str.name) + 1,str.name);
return *this;
}
}
~MyStr()
{
cout << "deconstructor" << endl;
delete name;
}
private:
char *name;
int id;
};
void main()
{
MyStr str1(1,"Jack");
MyStr str2;
str2 = str1;
MyStr str3 = str2;
return;
}
运行结果:
分析:
如果将上述例子显示提供的拷贝函数注释掉,然后同样执行MyStr str3 = str2;
语句,此时调用默认的拷贝构造函数,它们指向内存中的同一区域。
这样会有两个致命错误:
1)、str2修改name时,str3的name也会被修改;
2)、当执行str2和str3的析构函数时,会导致同一内存区域释放两次,程序崩溃。
所以,必须通过显示提供拷贝构造函数以避免这样的问题,如上述例子,先判断被拷贝者的name是否为空,若否,delete name,然后为name重新申请空间,再将拷贝者name中的数据拷贝到被拷贝者的name中,这样,str2.name和str3.name各自独立,避免了上面两个错误。赋值运算符重载函数也是同样的道理。
三、赋值运算符重载函数只能是类的非静态的成员函数
1、因为静态成员函数只能操作类的静态成员,无法操作类的非静态成员,可以参考静态成员变量和静态成员函数在C++类中的作用来进行理解;
2、避免二义性
当程序没有显示提供一个以本类或者本类的引用为参数的赋值运算符重载函数时,编译器会自动提供一个。现在假设C++允许友元函数定义的赋值运算符重载函数,而且以引用为参数,与此同时,编译器也提供一个默认的赋值运算符重载函数(由于友元函数不属于这个类,所以此时编译器会自动提供一个)。但是当再执行类似str2 = str1;
这样的代码时,编译器就困惑了。
为了避免这样的二义性,C++强制规定,赋值运算符重载函数只能定义为类的成员函数,这样编译器就能判断是否需要提供默认版本了。