运算符重载

121 阅读3分钟

「这是我参与2022首次更文挑战的第20天,活动详情查看:2022首次更文挑战

❗ 引入 ❕

class Date
{
public:
	Date(int year = 0, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;	
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1(2021, 10, 11);
	Date d2(2020, 11, 11);
	Date d3(2021, 11, 11);
	
	//d1 == d2;//err
	
	return 0;
}

📝 说明

d1 == d2 ❓

运算符默认都是给内置类型变量用的。

自定义类型的变量想用这些运算符,得自己运算符重载。

运算符重载指的是需要自己写一个函数实现这里运算符的行为。


C++ 为了增强代码的可读性引入了运算符重载,运算符重载是具有特殊函数名的函数,函数里的实现就是行为。它也具有其返回值类型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。

函数名字为:关键字 operator 后面接需要重载的运算符符号。

函数原型:返回值类型 operator 操作符 (参数列表)

注意:对于参数列表的个数有几个是根据操作符来确定的,比如 == 就需要两个参数;参数类型就是你要操作的对象类型;返回值类型就是运算符运算后的返回值。

class Date
{
public:
	Date(int year = 0, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;	
	}
//private:
public://这里破坏封装使得可以在类外访问成员
	int _year;
	int _month;
	int _day;
};
bool operator==(const Date& x1, const Date& x2)
{
	return x1._year == x2._year
		&& x1._month == x2._month
		&& x1._day == x2._day;
}
int main()
{
	Date d1(2021, 10, 11);
	Date d2(2020, 11, 11);
	Date d3(2021, 11, 11);
	
	operator==(d1, d2);//可以这样调用,但是这样可读性很差,还不如写一个函数
	d1 == d2;//同上,如果没有重载会报错,如果重载了它会转换为 operator==(d1, d2);
	return 0;
}

📝 说明

怎么解决上述代码中利用破坏了封装,使得运算符重载的函数可以访问私有成员 ❓

  1、友,但不推荐。

  2、将函数重载写在类里,写成一个成员函数。

class Date
{
public:
	Date(int year = 0, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;	
	}
	//bool operator==(const Date& x1, const Date& x2)//err
	//{
	//	return x1._year == x2._year
	//		&& x1._month == x2._month
	//		&& x1._day == x2._day;
	//}
	
	//bool operator==(Date* this, const Date& x)
	bool operator==(const Date& x)//ok
	{
		return _year == x._year
			&& _month == x._month
			&& _day == x._day;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1(2021, 10, 11);
	Date d2(2020, 11, 11);
	Date d3(2021, 11, 11);
	
	d1.operator==(d2);
	d1 == d2;//同上,编译器会自动识别转换为d1.operator==(d2); -> d1.operator(&d1, d2);
	return 0;
}

📝 说明

当我们把函数重载写进类里时,error C2804:operator== 的参数太多了 ❓

这里的矛盾点是运算符重载函数的参数是根据运算符来确定的但是对于类里的函数成员它会有一个 this 指针。解决方法就是少写一个参数。

对于d1 == d2 ❓

编译器会自动识别转换,如果是全局函数那么它会转换成 operator==(d1, d2);;如果是成员函数那么它会转换成 d1.operator(d2);。不管是全局还是成员一般我们都是直接写 d1 == d2。

观察 d1.operator==(d2); 和 d1 == d2; 的汇编代码 ❗

我们不一定需要会写汇编,但是需要勉强看懂 在这里插入图片描述

❗ 照虎画猫写一个d1<d2 ❕

class Date
{
public:
	Date(int year = 0, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;	
	}
	bool operator<(const Date& x)//ok
	{
		if(_year < x._year)
			return true;
		else if(_year == x._year && _month < x._month)
			return true;
		else if(_year == x._year && _month == x._month && _day < x._day)
			return true;
		else
			return false;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1(2021, 10, 11);
	Date d2(2020, 11, 11);
	Date d3(2021, 11, 11);
	
	cout << d1 < d2 << endl;//err
	cout << (d1 < d2) << endl;//ok,d1.operator<(d2); -> d1.operator<(&d1, d2);
	return 0;
}

📝 说明

上面使用 cout 输出结果时,出现了问题。<< 是流插入运算符,它的优先级比较高 (从上面看比 <、= 高,比 () 低),这里 cout 会先输出 d1,然后再 < ??? 在这里插入图片描述


⚠ 注意

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

2️⃣ 重载操作符必须有一个类类型或者枚举类型的操作数。

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

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

5️⃣ .* 、:: 、sizeof 、?: 、. ,注意以上 5 个运算符不能重载。这个经常在笔试选择题中出现,注意 * 是可以重载的,这里不能重载的是 .*。