C++ 运算符重载

C++ 为了增强代码的可读性引入了运算符重载 运算符重载是具有特殊函数名的函数 ,也具有其返回值类 型,函数名字以及参数列表,其返回值类型与参数列表与普通的函数类似。
函数名字为:关键字 operator 后面接需要重载的运算符符号
函数原型: 返回值类型  operator 操作符 ( 参数列表 )
那么为什么要有运算符重载?
答:对于我们平常所见的运算符(+,-,>,<....)在系统库函数中都已经存在,可以直接用于我们常用的数据类型,但对于自定义数据类型,比如用Date 定义的对象d1 和 d2,对于这俩个对象进行相加或者相减操作,到底是按照这个对象中的那个数据进行操作,就会出现错误,所以C++提供了运算符重载,来满足我们对类对象进行的操作。

一、运算符重载引入

先看一个简单的例子:
#include<iostream>
using namespace std;
class Date
{
public:
    //构造函数
	Date(int year=1999,int month=11,int day=23)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	int _year;
	int _month;
	int _day;
};
//重载运算符 == (重载为全局函数)
bool operator==(const Date& d1, const Date& d2)
{
	return d1._day == d2._day &&
		d1._month == d2._month &&
		d1._year == d2._year;
}
int main()
{
	Date d1(2003, 6, 21);
	Date d2(2004, 12, 6);
	if (d1 == d2)
	{
		cout << "d1==d2" << endl;
	}
	else
	{
		cout << "d1!=d2" << endl;
	}
	return 0;
}

根据这个例子,判断d1对象和d2对象是否相同,我们可以将 == 重载为全局函数,但此时必须要求类中的数据变量时公有成员

 对于这种情况,我们可以将运算符重载为类的成员函数(这种方法在之后的学习中使用最多)

#include<iostream>
using namespace std;
class Date
{
public:
	Date(int year=1999,int month=11,int day=23)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	bool operator==(const Date& d)
	{
		return _day == d._day &&
			_month == d._month &&
			_year == d._year;
        //也可以写为
        /*return this->_day == d._day &&
			this->_month == d._month &&
			this->_year == d._year;*/
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1(2003, 6, 21);
	Date d2(2004, 12, 6);
	if (d1 == d2)
	{
		cout << "d1==d2" << endl;
	}
	else
	{
		cout << "d1!=d2" << endl;
	}
	return 0;
}

对比这俩种重载的情况,我们可以发现,他们的参数数目不同,这也是他们之间的一个区别

1.将运算符重载为全局函数时,根据该运算符的自身情况(单目或者双目),来决定参数的个数,单目运算符需要一个操作数,则参数为一个;双目运算符需要两个操作数,则参数需要两个

2.将运算符重载为类的成员函数时,参数个数等于运算符的目数减一 (因为类的成员函数中隐含一个this指针)

二、赋值运算符(重点)

赋值运算符(=)只能重载为 类的成员函数 不能重载为全局函数

 我们发现编译报错,为什么会出现这种情况呢?

答:赋值运算符如果不显式实现,编译器会生成一个默认的(简单说,就是赋值运算符可以不被重载,编译器会在我们用户没有写重载函数的情况下,自动生成一个默认的重载函数)此时用户再在类外自己实现一个全局的 赋值运算符重载,就和编译器在类中生成的默认赋值运算符重载冲突了,故赋值运算符重载只能是类的 成员函数。

其次,我们还得注意一个问题:

既然编译器都自动给我们生成一个默认的赋值运算符重载函数了,我们还需要自己实现吗?

答:当然需要,对于上面的Date类,其中没有涉及到内存资源的管理,所以并没有什么影响;但是

一旦涉及到资源管理,就必须要我们自己实现。(在后期会细讲)

所以对于上述代码我们可以将其修改为:

 但是对于这个代码来说,可以实现d2=d1,即d1对d2的赋值,但如果将赋值语句改变为 d3=d2=d1,就会报错

 对于d3=d2=d1这种赋值,由于上述我们在实现运算符重载的时候没有设置返回值,即当d1给d2付完值之后,没有任何东西返回,这时候在给d3赋值,就会出错,所以我们可以修改代码如下:

#include<iostream>
using namespace std;
class Date
{
public:
	Date(int year=1999,int month=11,int day=23)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	
	Date& operator=(Date& d)
	{
		d._year = _year;
		d._month =_month;
		d._day = _day;
		return d;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1(2003, 6, 21);
	Date d2;
	Date d3;
	d3 = d2 = d1;
	return 0;
}

 

 三、单目运算符重载

1.重载前置++

根据我们上述讲述,我们可以简单实现代码:

#include<iostream>
using namespace std;
class Date
{
public:
	Date(int year=1999,int month=11,int day=23)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	//重载前置++
	Date& operator++()
	{
		_day++;
		return *this;
	}
	void display()
	{
		cout << _year << "/" << _month << "/" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
int main()
{
	Date d1(2003, 6, 21);
	++d1;
	d1.display();
	return 0;
}

那么当我们要重载后置++时候,是不是和前置++一模一样了,答案当然是否定的,那怎么进行区别呢,对于重载后置++的函数,又该怎么样写呢?

C++对于这种这种情况,为了让前置++和后置++都可以实现重载,规定:后置++在重载的时候多增加一个int类型的参数,但调用该函数时候,该参数不传递,编译器自动传递

代码演示:

#include<iostream>
using namespace std;
class Date
{
public:
	Date(int year=1999,int month=11,int day=23)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	//重载前置++
	Date& operator++()
	{
		_day++;
		return *this;
	}
	//重载后置++
	Date operator++(int)
	{
		Date temp(*this);
		this->_day++;
		return temp;
	}
	//重载赋值运算符 =
	Date& operator=(Date& d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}
	void display()
	{
		cout << _year << "/" << _month << "/" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main()
{
	Date d1(2003, 6, 21);
	++d1;
	d1.display();
	Date d2=d1++;
	d2.display();
	d1.display();
	return 0;
}

运行结果:

 对于上述代码还需要注意一个问题:

 四、常见的运算符重载(简单日期类实现)

#include<iostream>
using namespace std;
class Date {
public:
	// 获取某年某月的天数
	int GetMonthDay(int year, int month)
	{
		static int days[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
		int day = days[month];
		if (month == 2 && ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)))
		{
			day+=1;
		}
		return day;
	}
	// 全缺省的构造函数
	Date(int year = 1900, int month = 1, int day = 1)
		:_year(year),
		 _month(month),
		_day(day)
	{
		int m = GetMonthDay(_year, _month);
		if (_year<=0||_month > 13 || _month<1 || _day<=0||_day>m)
		{
			cout << "构造日期不符合,默认构造日期为1900/1/1" << endl;
			_year = 1900;
			_month = 1;
			_day = 1;
		}
	}

	// 拷贝构造函数
    // d2(d1)
	Date(const Date& d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}

	// 赋值运算符重载
    // d2 = d3 ------> d2.operator=(&d2, d3)
	Date& operator=(const Date& d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;

		return *this;
	}

	// 析构函数
	~Date()
	{
		cout << "~Date()" <<this<< endl;
	}

	// 日期+=天数
	Date& operator+=(int day)
	{
		_day += day;
		while (_day > GetMonthDay(_year, _month))
		{
			_day -= GetMonthDay(_year, _month);
			_month += 1;
			if (_month > 12)
			{
				_year += 1;
				_month = 1;
			}
		}
		return *this;
	}
	// 日期+天数
	Date& operator+(int day)
	{
		//用(*this)拷贝构造一个对象temp
		Date temp(*this);
		temp._day += day;

		//如果加完天数之后的结果比当前月份的天数大,说明不符合日期规则
		//进行修改
		while (temp._day >temp.GetMonthDay(temp._year, temp._month))
		{
			//1.将当前天天数 减去 当前月份的天数
			temp._day -= temp.GetMonthDay(temp._year, temp._month);
			//2.将当前月份加1
			temp._month += 1;
			//如果加1后月份的值大于12,不符合规则
			//将当前年加1,当前月份变为1月,表示下一年的一月份
			if (temp._month > 12)
			{
				temp._year += 1;
				temp._month = 1;
			}
		}
		return temp;
	}

	// 日期-天数
	Date& operator-(int day)
	{
		//用(*this)拷贝构造一个对象temp
		Date temp(*this);
		temp._day -= day;
		
		//2022年1月10号 - 20天
		//如果减完天数后的值小于等于0,说明不符合日期规则

		while (temp._day<=0)
		{
			//1.将当前月份减1
			temp._month -= 1;
			//如果当前月份减1后为0,不符合规则
			//将当前年减1,月份值赋值为12,表示上一年的12月份
			if (temp._month == 0)
			{
				temp._year -= 1;
				temp._month = 12;
			}
			//3.将当前天数 + 当前月份的总天数
			temp._day += temp.GetMonthDay(temp._year, temp._month);
		}
		return temp;
	}
	// 日期-=天数
	Date& operator-=(int day)
	{
		_day -= day;
		while (_day <= 0)
		{
			_month -= 1;
			if (_month == 0)
			{
				_year -= 1;
				_month = 12;
			}
			_day += GetMonthDay(this->_year, this->_month);
		}
		return *this;
	}

	// 前置++
	Date& operator++()
	{
		_day += 1;
		if (_day > GetMonthDay(_year, _month))
		{
			_day = 1;
			_month += 1;
			if (_month > 12)
			{
				_year++;
				_month = 1;
			}
		}
		return *this;
	}
	// 后置++
	Date& operator++(int)
	{
		Date temp(*this);
		_day++;
		if (_day > GetMonthDay(_year, _month))
		{
			_day = 1;
			_month += 1;
			if (_month > 12)
			{
				_year++;
				_month = 1;
			}
		}
		return temp;
	}

	// 后置--
	Date operator--(int)
	{
		Date temp(*this);
		_day--;
		if (_day<=0)
		{
			_month--;
			if (_month == 0)
			{
				_month = 12;
				_year--;
			}
			_day+=GetMonthDay(_year, _month);
		}
		return temp;
	}
	// 前置--
	Date& operator--()
	{
		--_day;
		if(_day <=0)
		{
			_month -= 1;
			if (_month == 0)
			{
				_year--;
				_month = 12;
			}
			_day += GetMonthDay(_year, _month);
		}
		return *this;
	}

	// >运算符重载
	bool operator>(const Date& d)const
	{
		if (_year > d._year)
		{
			return true;
		}
		else if (_year == d._year && _month > d._month)
		{
			return true;
		}
		else if (_year == d._year && _month == d._month && _day > d._day)
		{
			return true;
		}
		return false;
	}
	// ==运算符重载
	bool operator==(const Date& d)
	{
		if ((*this) != d)
		{
			if (this->_year == d._year &&
				this->_month == d._month &&
				this->_day == d._day)
			{
				return true;
			}
			else
			{
				return false;
			}
		}
		return true;
	}
	// >=运算符重载
	bool operator >= (const Date& d)const
	{
		if (_year > d._year)
		{
			return true;
		}
		else if (_year == d._year && _month > d._month)
		{
			return true;
		}
		else if (_year == d._year && _month == d._month && _day > d._day)
		{
			return true;
		}
		if (this->_year == d._year && this->_month == d._month && this->_day == d._day)
		{
			return true;
		}
		return false;
	}

	// <运算符重载
	bool operator < (const Date& d)
	{
		if (_year < d._year)
		{
			return true;
		}
		else if (_year == d._year && _month < d._month)
		{
			return true;
		}
		else if (_year == d._year && _month == d._month && _day < d._day)
		{
			return true;
		}
		return false;
	}
	// <=运算符重载
	bool operator <= (const Date& d)
	{
		if (_year < d._year)
		{
			return true;
		}
		else if (_year == d._year && _month < d._month)
		{
			return true;
		}
		else if (_year == d._year && _month == d._month && _day < d._day)
		{
			return true;
		}
		if (this->_year == d._year && this->_month == d._month && this->_day == d._day)
		{
			return true;
		}
		return false;
	}
	// !=运算符重载
	bool operator != (const Date& d)
	{
		if (_year != d._year || _month != d._month || _day != d._day)
		{
			return true;
		}
		return false;
	}

	//友元函数重载输入输出
	friend ostream& operator<<(ostream& cout, Date& d)
	{
		cout << d._year << "/" << d._month << "/"<<d._day;
		return cout;
	}
	friend istream& operator>>(istream& cin, Date& d)
	{
		cin >> d._year >> d._month >> d._day;
		return cin;
	}

	void Print()
	{
		cout << _year << "/" << _month << "/" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};
void Test1()
{
	Date d1;
	cin >> d1;
	Date d2;
	cin >> d2;
	cout << d1 << " " << d2 << endl;
	d1 += 872;
	d2 -= 900;
	cout <<"d1=" << d1 <<" " << "d2=" << d2 << endl;
	cout << "d1>d2: " << (d1 > d2)<< endl;

	Date d3=d1++;
	Date d4=--d2;
	cout << "d3=" << d3 <<" "<<"d4="<<d4<< endl;
	cout << "d3==d4: " << (d3 == d4) << endl;
	cout << "d1=" << d1 << " " << "d2=" << d2 << endl;
	cout << "d1!=d2: " << (d1 != d2) << endl;
}
int main()
{
	Test1();
	return 0;
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值