cpp_10.1类和对象中(十) --- 赋值运算重载符 遗留详解

1 日期类包含所学的四种函数

#include <iostream>
using namespace std;

#if 0
class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	Date(const Date& d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}

	~Date()
	{}

	//左操作数是this指向的调用函数的对象
	bool operator==(const Date& d)
	{
		return _year == d._year &&
			   _month == d._month &&
			   _day == d._day;
	}

	
private:
	int _year;
	int _month;
	int _day;
};


int main()
{
	Date d1(2020, 10, 14);
	Date d2(2020, 10, 15);

	if (d1 == d2)
	{
		;
	}

	return 0;
}
#endif

如果不写==赋值运算符重载,d1 == d2 就报错。

2 注意

2.1 不能通过连接其他符号来创建新的操作符:比如operator@

	// 注意:不能臆造运算符来进行重载
	void operator@()
	{

	}

@下面报错

2.2 重载操作符必须有一个类类型或者枚举类型的操作数

int operator+(int left, int right)
{
	return left + right;
}

在这里插入图片描述在这里插入图片描述

上述代码中,重载操作符都是类置类型int型,并且还是无限递归,所以错误。再怎么重载也不可能重载出其他的运算符

2.3 用于内置类型的操作符,其含义不能改变,例如:内置的整型+,不 能改变其含义

#if 0
class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	Date(const Date& d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}
	
	~Date()
	{}

	bool operator==(const Date& d)
	{
		return _year == d._year &&
			   _month == d._month &&
			   _day == d._day;
	}

	Date& operator+(int days)
	{
		_day += days;
		return *this;
	}

private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1(2020, 10, 14);
	Date d2(2020, 10, 15);

	d2 = d1 + 10;
	//d1值也应该不变,注意该处的d1传过去类似于this当前对象,所以形参应该是int days
	
	int a = 10;
	int b = 20;
	int c = 0;
	c = a + b;
	//此时a,b值不变
	return 0;
}
#endif

上述该代码:看似没错,但对于+赋值操作符函数,其改变了原先的d1值。
在这里插入图片描述

其实应该是对+=进行赋值操作符函数。
修改代码:

Date& operator+(int days)
{
		_day += days;
		return *this;
}

再举个例子,本来是加法运算,写成减法的逻辑。
代码如下:

//此处this相当于是d1
Date operator+(/*Date* const this,*/int days)
	{
		Date temp(*this);
		temp._day -= days;
		return temp;
	}

在这里插入图片描述

实际应该是:

Date operator+(/*Date* const this,*/int days)
	{
		Date temp(*this);
		temp._day += days;
		return temp;
	}

2.4 作为类成员的重载函数时,其形参看起来比操作数数目少1成员函数的操作符有一个默认的形参this,限定为第一个形参

#if 0
class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	Date(const Date& d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}
	
	~Date()
	{}
	
	//类中时可以省略
	Date operator+(/*Date* const this,*/int days)
	{
		Date temp(*this);
		temp._day += days;
		return temp;
	}

private:
	int _year;
	int _month;
	int _day;
};


//全局时,需要写全。
Date operator-(const Date& d, int days)
{
	Date temp(d);
	temp._day -= days;//如果_day是类中私有成员,这块不能访问,此时将其改为公有的。
	return temp;
}


int main()
{
	Date d1(2020, 10, 14);
	Date d2(2020, 10, 15);
	
	d2 = d1 - 1;
	return 0;
}
#endif

第一种:类中时可以省略,省略了Date* const this

Date operator+(/*Date* const this,*/int days)
	{
		Date temp(*this);
		temp._day += days;
		return temp;
	}

第二种:全局时,需要将参数写全。

Date operator-(const Date& d, int days)
{
	Date temp(d);
	temp._day -= days;//如果_day是类中私有成员,这块不能访问,此时将其改为公有的。
	return temp;
}

2.5 5个运算符不能重载。

.*
::
sizeof
?:
.

3 赋值运算符

#if 0
class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	Date(const Date& d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}

	~Date()
	{}

	// =赋值运算符函数应注意:
	//1、返回值
	//2、参数类型
	//3、检测是否自己给自己赋值
	//4、返回*this
	//5、一个类如果没有显式定义赋值运算符重载,编译器也会生成一个,完成对象按字节序的值拷贝。
	Date& operator=(const Date& d)
	{
		// this是=运算符的左操作数,d是=运算符的右操作数
		//cout << this << "=" << &d << endl;

		if (this != &d)
		{
			_year = d._year;
			_month = d._month;
			_day = d._day;
		}
		
		return *this;
		//return d;
	}

private:

	int _year;
	int _month;
	int _day;
};




int main()
{
	Date d1(2020, 10, 14);
	Date d2(2020, 10, 15);
	Date d3(2020, 10, 16);
	
	//1、d1 = d2 等价===》d1.operator=(d2)
	d1 = d2;
	d1.operator=(d2);

	//d1 = d2 = d3 等价===》d1.operator=(d2.operator=(d3));
	d1 = d2 = d3; 
	
	
	//2、自己给自己赋值没有意义,但是有时写成引用的形式,会没有看到
	d1 = d1;
	Date& d4 = d1;
	d4 = d1;


	//3、变量赋值
	//连续赋值-->其返回值是什么
	int a = 10;
	int b = 20;
	int c = 0;

	a = b;
	a = c;
	a = b = c;
	return 0;
}
#endif

3.1 参数问题:引用还是传值

void operator=(const Date d)
{
	cout << this << "=" << &d << endl;
}
int main()
{
	Date d1(2020, 10, 14);
	Date d2(2020, 10, 15);
	d1 = d2;
	return 0;
}

在这里插入图片描述

调试时,往里面走一步,发现其d1 = d2 ,调用的是拷贝构造函数。
原因:参数是按照值的方式进行传递。按照值的方式进行传递,会在传参期间生成一份拷贝。拷贝一个新的对象,这种传参效率太低了。解决这种问题,对其加引用,引用就相当于别名。d就相当于是实参的别名。

在这里插入图片描述

修改后代码如下:

void operator=(const Date& d)
{
	// this是=运算符的左操作数,d是=运算符的右操作数
	cout << this << "=" << &d << endl;
}

运行结果可以看出:将d2的地址给d1了。

在这里插入图片描述

最终结果:引用

void operator=(const Date& d)
	{
		// this是=运算符的左操作数,d是=运算符的右操作数
		//cout << this << "=" << &d << endl;
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}

3.2 返回值:无返回值还是Date& 还是Date

int main()
{
	//c给b赋值,然后b给a赋值
	a = b;
	a = c;
	a = b = c;
}

在这里插入图片描述

变量可以进行连续赋值,那对象呢?

void operator=(const Date& d)
{
		// this是=运算符的左操作数,d是=运算符的右操作数
		//cout << this << "=" << &d << endl;
		_year = d._year;
		_month = d._month;
		_day = d._day;
}
int main()
{
	Date d1(2020, 10, 14);
	Date d2(2020, 10, 15);
	Date d3(2020, 10, 16);

	d1 = d2;
	d1.operator=(d2);
	d1 = d2 = d3; //  ===》d1.operator=(d2.operator=(d3));
	return 0;
}

在这里插入图片描述

不支持连续赋值。

d1 = d2;
d1.operator=(d2);

两个代码是等价的,反汇编代码如下:

在这里插入图片描述

d1 = d2 = d3; 
d1.operator=(d2.operator=(d3));

因此:返回值类型应该是个日期类型:Date,但是,应该是Date& 还是 Date

  • 一般,设计函数期间,返回值类型还是参数,涉及到自定义类型时,能先用Date&,先使用Date&.如果不用引用,有可能调用的时拷贝构造函数,在进入赋值运算符函数。

最终结果:Date&

Date& operator=(const Date& d)
{
	_year = d._year;
	_month = d._month;
	_day = d._day;
}

3.3 返回值问题:return *this还是return d

Date& operator=(const Date& d)
{
		// this是=运算符的左操作数,d是=运算符的右操作数
		//cout << this << "=" << &d << endl;

		_year = d._year;
		_month = d._month;
		_day = d._day;
		
		return *this;
		return d;
}

int main()
{
	Date d1(2020, 10, 14);
	Date d2(2020, 10, 15);
	Date d3(2020, 10, 16);
	
	//1、d1 = d2 等价===》d1.operator=(d2)
	d1 = d2;
	d1.operator=(d2);

	//d1 = d2 = d3 等价===》d1.operator=(d2.operator=(d3));
	d1 = d2 = d3; 
	return 0;
}

不管返回是return *this还是return d,都会达到预期的效果。
在这里插入图片描述
但是,哪个效果到底更好一些呢?
针对这个语句
d1 = d2 = d3
d1.operator=(d2.operator=(d3));
d3传过去给d,d2相当于this,所以结果是返回return *this;更好。

*最终结果:return this;

Date& operator=(const Date& d)
{
		_year = d._year;
		_month = d._month;
		_day = d._day;
		return *this;
}

2.4 检测是否自己给自己赋值

#if 0
class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	Date(const Date& d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}

	~Date()
	{
	}
	
	Date& operator=(const Date& d)
	{
		if (this != &d)
		{
			_year = d._year;
			_month = d._month;
			_day = d._day;
		}
		return *this;
	}

private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1(2020, 10, 14);
	//自己给自己赋值没有意义,但是有时写成引用的形式,会没有看到
	d1 = d1;
	Date& d4 = d1;
	d4 = d1;
	return 0;
}

结论:自己给自己赋值没有意义,但是有时写成引用的形式,会没有看到,所以添加一个判断。

4 赋值运算符函数注意的点

=赋值运算符函数应注意:

  • 返回值
  • 参数类型
  • 检测是否自己给自己赋值
  • 返回*this
  • 一个类如果没有显式定义赋值运算符重载,编译器也会生成一个,完成对象按字节序的值拷贝。
Date& operator=(const Date& d)
{
	if (this != &d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}
	
	return *this;
}

在这里插入图片描述

5 ++ – 运算符函数

知识点7++ -- 运算符重载
#if 0
class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	Date(const Date& d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}

	~Date()
	{}

	// 前置++
	Date& operator++()
	{
		_day++;
		return *this;
	}

	// 后置++
	// 先使用 后加+1
	Date operator++(int)
	{
		Date temp(*this);
		_day++;
		return temp;
	}

	Date& operator--()
	{
		_day--;
		return *this;
	}

	// 后置--
	// 先使用 后-1
	Date operator--(int)
	{
		Date temp(*this);
		_day--;
		return temp;
	}

	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1(2020, 10, 14);
	Date d2(2020, 10, 15);
	
	//对象
	d2 = d1++;  // 后置++
	d2 = ++d1;  // 前置++

	//变量
	int a = 10;
	a++;
	a--;

	return 0;
}
#endif

5.1 前置++

Date& operator++()
{
	_day++;
	return *this;
}

5.2 后置++

Date operator++(int)
{
	Date temp(*this);
	_day++;
	return temp;
}

5.3 前置–

Date& operator--()
{
	_day--;
	return *this;
}

5.4 后置–

Date operator--(int)
{
	Date temp(*this);
	_day--;
	return temp;
}
  • 形参部分基本都是引用:不想通过外部实参改变形参,加上const就可以。
  • 返回类型,一般先加引用,看返回实体,如果是栈上的实体,就去掉引用。

5 & 运算符函数

#if 0
class Date
{
public:
	Date(int year = 1900, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	Date(const Date& d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}

	Date* operator&()
	{
		cout << this << endl;
		return this;
	}

private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1(2020, 10, 14);
	//没有实现&赋值运算符函数时,打印地址
	cout << &d1 << endl;
	//写一个&赋值运算符函数,见到&符号,就打印地址
	&d1;


	//不打印地址,此时因为是const修饰。
	const Date d2(2020, 10, 15);
	&d2;
	return 0;
}
#endif

往期链接:
cpp_9.3类和对象中(九) — 赋值运算重载符
cpp_9.2类和对象中(八) — 拷贝构造函数
cpp_9.1 类和对象中(七)—构造函数中,无参对象如何调用?底层如何实现?
cpp_8.2类和对象中(六) — 析构函数
cpp_8.1类和对象中(五) — 构造函数链接描述
cpp_8 类和对象上(四) — this遗留
cpp_7.1 类和对象上(三) — this
cpp_7类与对象上(二) — 类对象的存储方式
cpp_6.1类与对象上(一)— 类的引入

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值