[C++系列] 21. 运算符重载---''=''

1. 赋值运算符重载

赋值运算符重载即便不写,编译器也会默认自动生成,完成正常赋值。

class Date {
public:
	Date(int year, int month, int day) {
		_year = year;
		_month = month;
		_day = day;
	}
	Date(const Date& d) {
		_year = d._year;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main() {
	Date d1(2019, 4, 1);
	Date d2(2019, 3, 31);
	d2 = d1;
	system("pause");
	return 0;
}

2. 底层实现

class Date {
public:
	Date(int year, int month, int day) {
		_year = year;
		_month = month;
		_day = day;
	}
	Date(const Date& d) {
		_year = d._year;
	}
	void operator=(const Date& d1) { 
		if (this != &d1) {            // 自己不需要给自己赋值
			_year == d1._year;
			_month == d1._month;
			_day == d1._day;
		}
	}
private:
	int _year;
	int _month;
	int _day;
};

int main() {
	Date d1(2019, 4, 1);
	Date d2(2019, 3, 31);
	d2 = d1;
	system("pause");
	return 0;
}

当自己定义赋值运算符重载后,编译器就会对其进行调用,而不会再默认生成。在此,以成员函数的形式实现,由于不需要自己给自己赋值,在函数内部做一步判断即可。现在单步赋值能够进行,但是不能够连续赋值,代码需要进一步优化。

3. 连续赋值

赋值是从右向左进行赋值的,d3 = d2 = d1进行连续赋值时,d2 = d1赋值完毕后返回void。此时再进行赋值时,this相当于&d3,而d相当于void。那么就变成了d3 = void,即出错。在此应该用d2给d3进行赋值,应该传入上一次this指针指向的位置。即在最后应该传入*this,即应该return *this;

class Date {
public:
	Date(int year, int month, int day) {
		_year = year;
		_month = month;
		_day = day;
	}
	Date(const Date& d) {
		_year = d._year;
	}
	Date& operator=(const Date& d1) { 
		if (this != &d1) {            // 自己不需要给自己赋值
			_year == d1._year;
			_month == d1._month;
			_day == d1._day;
		}
		return *this;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main() {
	Date d1(2019, 4, 1);
	Date d2(2019, 3, 31);
	Date d3(2019, 3, 29);
	d3 = d2 = d1;
	system("pause");
	return 0;
}

4. 坑点

int main() {
	Date d1(2019, 4, 1);
	Date d2(2019, 3, 31);
	Date d3(2019, 3, 29);
        // 赋值运算符:改变已有对象的内容
	d3 = d2 = d1;
        // 拷贝构造:创建一个新的对象,内容和d1一样
	Date d4 = d1;            
	system("pause");
	return 0;
}

对于d4来讲,它还不存在,得先构造一个对象,而构造的内容就是d1,相当于把d1内容拷贝过来。所以调用拷贝构造函数。

明确一点:对于没有存在的对象,首先都需要调用构造函数对其进行创建。而赋值用于已经存在的对象中,改变已经存在的对象的内容。

赋值运算符主要有四点:
1)参数类型,
参数传值、传引用均可,它区别于拷贝构造,不会引发无穷递归。传值的话就进行一次拷贝,传引用的话就是外面对象本身,效率高一些。
2)返回值,返回*this,用于连续赋值。如果不想连续赋值返回void也没有关系。
3)检测是否自己给自己赋值。若对于指针来讲,或是有资源分配的情况下,如若未检测自己给自己赋值,可能会产生野指针。
4)返回*this。
5)一个类如果没有显式定义赋值运算符重载,编译器也会生成一个,完成对象按字节序的值拷贝(
赋值运算符重载,其实与拷贝构造函数很相似,它也是一个拷贝,编译器所默认生成的重载函数也存在浅拷贝的问题,遇到string类得自己进行实现了,针对Date类采用默认的即可)。

默认赋值运算符针对String类引发浅拷贝问题:

// 这里会发现下面的程序会崩溃掉?这里就需要我们以后讲的深拷贝去解决。
class String {
public:
    String(const char* str = "jack") {
        _str = (char*)malloc(strlen(str) + 1);
        strcpy(_str, str);
    }
 
    ~String() {
        cout << "~String()" << endl;
        free(_str);
    }
private:
    char* _str;
};
 
int main() {
    String s1("hello");
    String s2("world");
    
    s1 = s2;
}

有申请资源的操作,需要自己定义这两个成员函数。没有的话可以采用编译器默认生成的函数进行使用,否则会产生浅拷贝双层析构的问题。

猜你喜欢

转载自blog.csdn.net/yl_puyu/article/details/88947082