C++类和对象中:运算符重载+const成员函数+日期类的完善

news2025/7/16 22:58:32

C++类和对象中:运算符重载+const成员函数+日期类的完善

  • 一.为什么C++会有运算符重载这个语法呢?
    • 1.需求说明
    • 2.实现
      • 1.不规范的解决方案
        • 1.代码实现
        • 2.缺陷
    • 3.具体的解决方案:运算符重载
  • 二.运算符重载的语法形式
    • 1.语法形式
    • 2.private私有成员的解决方案
      • 1.封装出get函数,能够在类外读取对应成员变量的数值
      • 2.把运算符重载放到类内部
    • 3.其他比较运算符重载的代码
  • 三.赋值运算符重载
    • 1.赋值运算符重载为什么能够作为默认成员函数之一呢?
    • 2.赋值运算符的返回值类型
    • 3.赋值运算符重载的具体实现
    • 4.赋值运算符重载跟拷贝构造函数的区别与联系
    • 5.注意
  • 四.+和+=的运算符重载
    • 1.+=的运算符重载
      • 1.+=有没有返回值呢?
      • 2.+=的实现
    • 2.+的运算符重载
    • 3.可不可以让+=复用+呢?
    • 4.这两种方案哪一个更好呢?
    • 5.一个"坑点"
  • 五.++运算符重载
    • 1.前置++的运算符重载
    • 2.后置++
  • 六.日期跟天数相减的运算符重载
    • 1.怎么相减呢?
    • 2.-复用-=
  • 七.日期-日期计算天数
  • 八.流插入和流提取运算符重载
    • 1.流插入运算符重载的引出
    • 2.流插入运算符重载的类内版本
    • 3.类内版本的局限性
    • 4.友元
    • 5.想要连续进行流插入怎么办呢?
    • 6.流提取>>运算符重载
    • 7.C++为什么要设计流插入和流提取呢?
  • 九.const成员函数
    • 1.为什么要有const成员函数
    • 2.成员函数没有加const的意想不到的错误
    • 3.const成员函数定义的原则
    • 4.几个小问题
  • 十.取地址运算符重载
  • 十一.具体的解决方案(日期类的完善)
    • 1.缺省参数的一个常见错误
    • 2.Date.h
    • 3.Date.cpp

一.为什么C++会有运算符重载这个语法呢?

1.需求说明

有的时候对于某些类来说,我们会有一些需求让我们去实现一些函数,能够便捷快速地对该类的若干成员变量进行数据操作

以日期类为例,有些时候我们想要去判断两个日期谁大谁小,是否相等,计算两个日期之间相差多少天,计算某一个日期加上几天后的日期是多少等等等等的需求

2.实现

有需求,就会有一大堆解决方案.
我们这里以比较日期大小和Date类为例:

class Date
{
public:
	Date(int year = 1, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}
	void Print()
	{
		cout << _year << " " << _month << " " << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};

其中不太好的解决方案是:

1.不规范的解决方案

1.代码实现
bool Greater(const Date& d1, const Date& d2)
{
	if (d1._year > d2._year)
	{
		return true;
	}
	else if (d1._year == d2._year && d1._month > d2._month)
	{
		return true;
	}
	else if (d1._year == d2._year && d1._month == d2._month && d1._day > d2._day)
	{
		return true;
	}
	return false;
}

在这里插入图片描述
这里这么多报错呢?
因为我们的日期类的成员变量的访问权限是private,私有
在类外访问不到

怎么办呢?
这个我们先暂时抛开不谈,我们下面会回过头来解释解决方法的.
这里我们先把成员变量设置为公有
在这里插入图片描述
实现的挺好的啊,怎么啦?

2.缺陷

有一点不太好的地方,例如:
这个函数的名字叫做Greater(起的挺好)
但是:万一有人这么去定义名字呢?

bool dayu(const Date& d1, const Date& d2)
{//具体实现}
bool compare1(const Date& d1, const Date& d2)
{//具体实现}
如果是比较是否相等的函数
bool xiangdeng(const Date& d1, const Date& d2)
{//具体实现}
bool compare2(const Date& d1, const Date& d2)
{//具体实现}

这是不是不太好啊,如果跟一些外国人一起合作做一些项目开发什么的话
他们不一定能看懂啊:dayu,xiangdeng
而且compare1和compare2对我们中国人也不是很友好,没有人规定1就是大于,2就是小于啊…
所以为了便于代码的跨国际通用性,C++创始人就引入了运算符重载这一语法

3.具体的解决方案:运算符重载

下面我们让开始一起来探索运算符重载的奥秘吧
具体的解决代码在我们学完运算符重载之后我们会给出来的

二.运算符重载的语法形式

受到了数学中运算符的启发,C++创始人就想:

在C语言中运算符只能操作一些内置的数据类型,那么可不可以让它们也能操作一下自定义类型呢?
既然我们都已经决定要去规范一下大家的代码了,那么可不可以把数学符号引入到自定义类型的比较中来呢?
在这里插入图片描述

1.语法形式

bool operator>(const Date& d1, const Date& d2)
{
	if (d1._year > d2._year)
	{
		return true;
	}
	else if (d1._year == d2._year && d1._month > d2._month)
	{
		return true;
	}
	else if (d1._year == d2._year && d1._month == d2._month && d1._day > d2._day)
	{
		return true;
	}
	return false;
}

具体调用:

bool ret = operator>(d1, d2);

在这里插入图片描述
这个函数比起Greater来说只是改了一下名字
那么你可能会觉得,不过如此嘛
但是,惊喜在这里呢~:
在这里插入图片描述
具体调用:

bool ret = d1 > d2;

大于运算符真的可以对自定义类型进行比较了!
为什么呢?

C++创始人设计运算符重载时想:我都已经设计好运算符重载这一语法了,可是调用的话还要加个operator,还是有点麻烦啊,要不然就让编译器去把operator优化一下吧

让我们能够真的只用一个>就能比较两个自定义类型

2.private私有成员的解决方案

但是:我们之前提到过,这个类中的私有成员,类外不能访问,那么怎么办呢?

1.封装出get函数,能够在类外读取对应成员变量的数值

在这里插入图片描述
这里为什么会报错呢?因为我们在这里加了const修饰,导致d1和d2不能去调用对应的成员函数,因为编译器怕我们在成员函数中修改成员变量,违背了const的修饰

那我们就去掉const
在这里插入图片描述
成功运行
但是也太麻烦了吧,我还需要再去单独设计三个函数来保证类外可以读取类内部的数据
那如果我都不想让类外部读取到我类内部的数据呢?

2.把运算符重载放到类内部

不就这么简单吗,至于这么大费周章吗,我还以为能有什么高明的手段呢.
你错了:
在这里插入图片描述
然后我们把这个运算符重载移到类内部,
结果发现:这里竟然报错了

为什么会报出参数太多的错误来呢?
因为类的成员函数的参数列表中有一个隐含的this指针!!!

而且运算符重载有一个规定:

操作的数据个数要与参数个数匹配上

这里我们操作的数据个数是2个,可是参数个数有3个,因此报出了参数太多的错误

怎么修改呢?
把一个参数改为this指针
也就是这样:

bool operator>(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;
}

在这里插入图片描述

调用方法:
1.d1>d2;
2.d1.operator>(d2)  本质是:operator(&d1,d2);this指针指向d1

这就是>的运算符重载的版本

3.其他比较运算符重载的代码

下面一起给出==,!=,<=,>=,<的重载版本了
并且对构造函数传入的日期进行了规范化检查
其实只要我们写出>==或者<==
就可以借助于逻辑与或者逻辑或,逻辑取反操作符来进行复用

其中*this就是当前调用这个运算符函数的对象

class Date
{
public:
	Date(int year = 1, int month = 1, int day = 1)
	{
		//对传入的日期进行检查
		if ((year < 0) || (month < 1 || month>12) || (day<1 || day>GetMonthDay(year, month)))
		{
			//assert(false);//粗暴一点的方法
			Print();//本质是:this->Print();
			cout << "输入的日期为非法日期" << endl;
			exit(-1);
		}
		_year = year;
		_month = month;
		_day = day;
	}
	void Print()
	{
		cout << _year << " " << _month << " " << _day << endl;
	}
	bool operator>(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<=(Date& d)
	{
		return !(*this > d);
	}
	bool operator>=(Date& d)
	{
		return *this == d || *this > d;
	}
	bool operator<(Date& d)
	{
		return !(*this >= d);
	}
	bool operator==(Date& d)
	{
		return _year == d._year && _month == d._month && _day == d._day;
	}
	bool operator!= (Date& d)
	{
		return !(*this == d);
	}
private:
	int _year;
	int _month;
	int _day;
};

在这里插入图片描述
当我们引出了运算符重载这个知识点并且实现了>,==,等等运算符的重载之后

三.赋值运算符重载

在这里插入图片描述
接上我们类和对象中这个大模块的知识,下面我们来介绍赋值运算符重载这个类的默认成员函数

1.赋值运算符重载为什么能够作为默认成员函数之一呢?

为什么运算符重载中只有赋值运算符会成为类的6大默认成员函数之一呢?

我的理解是:
在这里插入图片描述
在这里插入图片描述

2.赋值运算符的返回值类型

那么赋值运算符重载的返回值类型是什么呢?
这就要看一下=这个运算符本身的返回值类型了
在这里插入图片描述
在这里插入图片描述
=这个运算符本身就可以这样连着写,因此b=c这个表达式返回的就是b本身
因此赋值运算符重载的返回值类型是该类对象的引用

3.赋值运算符重载的具体实现

具体代码:

Date5& operator=(Date5& d)
{
	//对传入的日期进行检查
	if ((d._year < 0) || (d._month < 1 || d._month>12) || (d._day<1 || d._day>GetMonthDay(d._year, d._month)))
	{
		//assert(false);//粗暴一点的方法
		cout << "拷贝对象的日期为非法日期: ";
		d.Print();
		cout << "无法进行拷贝" << endl;
		exit(-1);
	}
	_year = d._year;
	_month = d._month;
	_day = d._day;
	return *this;
}

在这里插入图片描述
我们来看一下拷贝构造函数的代码

Date5(Date5& d1)
{
	//对传入的日期进行检查
	if ((d1._year < 0) || (d1._month < 1 || d1._month>12) || (d1._day<1 || d1._day>GetMonthDay(d1._year, d1._month)))
	{
		//assert(false);//粗暴一点的方法
		cout << "拷贝对象的日期为非法日期: ";
		d1.Print();
		cout << "无法进行拷贝" << endl;
		exit(-1);
	}
	_year = d1._year;
	_month = d1._month;
	_day = d1._day;
}

4.赋值运算符重载跟拷贝构造函数的区别与联系

在这里插入图片描述
关于拷贝构造函数,大家可以看我的另一篇博客:
C++类和对象中(构造函数,析构函数,拷贝构造函数)详解
里面对拷贝构造函数的来龙去脉进行了详细的讲解

5.注意

在这里插入图片描述
下面给大家调试一下,我们按F11进入函数
在这里插入图片描述
证明完毕
尽管:
Date d2 = d;看起来像是调用运算符重载函数
但是d2是一个尚未被建立的对象,当前正在用d的数据来对d2进行构造,也就是拷贝构造

这也就印证了这句话:

拷贝构造函数:是利用一个已经存在的对象去拷贝一份还未被创建的对象
而赋值运算符重载:是两个已经存在的对象之间进行拷贝赋值

四.+和+=的运算符重载

1.+=的运算符重载

1.+=有没有返回值呢?

对于一个内置类型来说,我们在这里以两个int类型为例,来探讨一下两个int类型进行+=计算有没有返回值
在这里插入图片描述
可见,是有返回值的
这里我们要实现的是让一个日期+=天数

2.+=的实现

因为每个月份的天数都不同,而且二月在闰年是29天,在平年是28天
所以我们有必要实现一个函数来获取当前月份有多少天

int GetMonthDay(int year, int month)
{
	int MonthArray[13] = { 0,31,28,31,30,31,30,31,30,31,31,30,31 };
	if (month == 2 && ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)))
	{
		MonthArray[month]++;
	}
	return MonthArray[month];
}
Date operator+=(int day)
{
	_day += day;
	while (_day > GetMonthDay(_year, _month))
	{
		_day -= GetMonthDay(_year, _month);
		_month++;
		if (_month == 13)
		{
			_year++;
			_month = 1;
		}
	}
	//从这里我们就可以看出*this在某些情况下是需要在成员函数内部显式使用的
	return *this;//*this就是调用+=这个运算符的变量
}

从这里我们就可以看出*this在某些情况下是需要在成员函数内部显式使用的

在这里插入图片描述
可不可以再优化一些呢?
其实我们这个函数是可以传引用返回的,因为:

Date d1(2023, 10, 23);
d1 += 70;
返回的是d1,d1本身并不会随着+=这个运算符函数的销毁而销毁的,因此可以传引用返回
这也是引用作为返回值的一大应用
Date& operator+=(int day)
{
	_day += day;
	while (_day > GetMonthDay(_year, _month))
	{
		_day -= GetMonthDay(_year, _month);
		_month++;
		if (_month == 13)
		{
			_year++;
			_month = 1;
		}
	}
	return *this;//*this就是调用+=这个运算符的变量,而且这个*this出了这个+=函数之后依然存在,所以可以返回引用
}

在这里插入图片描述

2.+的运算符重载

Date operator+(int day)
{
	Date d2(*this);//这里调用拷贝构造函数
	d2._day += day;
	while (d2._day > GetMonthDay(d2._year, d2._month))
	{
		d2._day -= GetMonthDay(d2._year, d2._month);
		d2._month++;
		if (d2._month == 13)
		{
			d2._year++;
			d2._month = 1;
		}
	}
	return d2;
}

那么问题来了,这个+的运算符重载可以传引用返回吗?
当然不可以
为什么呢?

d2是+这个运算符函数中的局部变量,随着+这个函数栈帧的销毁,d2也会随之销毁
因此传只能传值返回,而且返回的不是d2,而是d2的一份临时拷贝
在这里插入图片描述
其实这里也不需要这么麻烦,我们是可以复用+=这个运算符的

	Date operator+(int day)
	{
		Date d2(*this);//这里调用拷贝构造函数
		d2 += day;
		return d2;
	}

在这里插入图片描述

3.可不可以让+=复用+呢?

当然可以啦,我们刚刚介绍的赋值运算符重载函数就能派上用场了

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

4.这两种方案哪一个更好呢?

在这里插入图片描述
对于+复用+=来说
+=:拷贝了0次
+:拷贝了2次:分别是:
在这里插入图片描述
对于+=复用+来说
+拷贝了2次:分别是:
在这里插入图片描述
+=拷贝了3次:分别是:
在这里插入图片描述

因此使用+复用+=更好
同理,使用-复用-=更好

5.一个"坑点"

在+=复用+的版本中
但是当我们写好了这个复用函数后会发现编译器报错
在这里插入图片描述
这是为什么呢?

是因为我们所写的赋值运算符重载函数

Date& operator=(Date& d);

这个参数列表中没有加上const修饰
而*this=*this+day;
这个表达式的右值返回的是一个临时变量,而临时变量具有常性
也就可以认为这个临时变量具有const修饰

然后继续调用赋值运算符重载函数,可是赋值运算符重载函数的参数并没有const修饰,因此出现了权限的放大问题,进而报错

同理,如果我们这样调用拷贝构造函数也是如此

Date d((*this) + day);

在这里插入图片描述
当我们加上const之后就没事了
在这里插入图片描述

五.++运算符重载

1.前置++的运算符重载

我们可以直接复用+=运算符

//前置++:拷贝0次
Date& operator++()
{
	*this += 1;
	return *this;
}

这里注意:
因为+=已经考虑了不同月份的具体天数不同,因此我们这里就不需要去管不同月份天数的影响了
在这里插入图片描述

2.后置++

C++创始人规定
前置++这样去定义:

Date& operator++()

后置++这样去定义:

Date operator++(int)

这里就印证了我们之前在学习缺省参数的时候学习到的一点:

编译器允许函数的参数列表中的参数只写类型,不写名称

这个参数设计的目的就是为了区分前置++和后置++
那么为什么要设计为int呢?

没有为什么,C++创始人就是这么规定的

六.日期跟天数相减的运算符重载

1.怎么相减呢?

在这里插入图片描述
注意这里的红字
就是先
_day-=day;
然后向月份借天数,注意:向前一个月份借天数
直到_day>0为止

注意:
如果有人就是想要-=一个负数
那就相当于+=这个负数的相反数
因此我们在这里加了一个if先去判断

Date& operator-=(int day)
{
	//减一个负数==加上这个负数的相反数
	if (day < 0)
	{
		return (*this) += (-day);
	}
	_day -= day;
	while (_day <= 0)
	{
	//因为接下来要向前一个月份借天数,所以先让月份--
		_month--;
		if (_month == 0)
		{
			_month = 12;
			_year--;
		}
		_day += GetMonthDay(_year, _month);
	}
	return *this;
}

同理:对于+=,我们也可以去改进一下

Date& operator+=(int day)
{
	//加一个负数==减去这个负数的相反数
	if (day < 0)
	{
		return (*this) -= (-day);
	}
	_day += day;
	while (_day > GetMonthDay(_year, _month))
	{
		_day -= GetMonthDay(_year, _month);
		_month++;
		if (_month == 13)
		{
			_month = 1;
			_year++;
		}
	}
	return *this;
}

2.-复用-=

Date operator-(int day)
{
	Date d(*this);
	d -= day;
	return d;
}

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

七.日期-日期计算天数

还有最后一个需求
我们想要去计算两个日期之间相差多少天
因为每个月的天数都不相同,而且还有闰年的干扰
所以只能这么去计算相差多少天

int operator-(Date& d)
{
	Date max = *this;
	Date min = d;
	int flag = 1;//假设:*this大于d,左大于右     左-右
	//假设错误
	if (min > max)
	{
		max = d;
		min = *this;
		flag = -1;//右大于左
	}
	int day = 0;
	while (min < max)
	{
		//++min;//++min是代码实现是调用+=的,那我直接调用+=不好吗,更快
		min += 1;
		day++;
	}
	return flag * day;
}

在这里插入图片描述
我们在网上找一下日期转换工具测试一下
在这里插入图片描述
在这里插入图片描述

八.流插入和流提取运算符重载

1.流插入运算符重载的引出

我们之前提到过C++创始人对于C语言中的左移运算符<<和右移运算符>>
又分别添加了一种语法
规定<<不仅可以表示左移运算符,也可以表示流插入运算符
/>>不仅可以表示右移运算符,也可以表示流提取运算符

对于内置类型:
我们可以这样去打印:
在这里插入图片描述
说明流插入是可以自动识别类型的
那么我们可不可以这样做呢?
在这里插入图片描述
当然是做不了的
为什么呢?
因为当我们没有重载流插入运算符时,编译器是无法得知我们想要怎样打印这个自定义类型的
因此流插入运算符是无法识别自定义类型的

那么为什么流插入运算符能够自动识别内置类型呢?
在这里插入图片描述
其实是因为C++库中已经实现好了这些自定义类型的流插入运算符重载函数

而cout和cin的类型分别是:

cout的类型是:ostream
cin的类型是:istream

我们可以这么理解:
平常我们使用cout来打印数据时
例如:

cout<<100;

本质是调用了<<的一个重载版本:
我们可以理解为:cout是这样打印的
在这个运算符重载的类当中
cout被隐含的this指针传过去,而另一个参数就是我们要打印的这个100

这个<<的重载函数是这样的:
ostream& operator<<(int val);
cout.operator<<(100);

在这里插入图片描述

2.流插入运算符重载的类内版本

于是我们就可以写出<<的重载版本

void Date::operator<<(ostream& out)
{
	out << _year << " " << _month << " " << _day << endl;
}

这里我们用out作为参数,跟cout的名字区分一下
其实当我们调用cout<<d1;来打印的时候
我们会把cout传入并用out来接收,而且这个out的类型是一个引用,也就是说这个out就是cout的一个别名

然后我们去打印,但是发现了一个"奇怪"的现象

3.类内版本的局限性

在这里插入图片描述
使用cout<<d1;还是无法打印,为什么啊?
但是更奇怪的现象是:
在这里插入图片描述
使用d1<<cout就能打印了
这是为什么呢?

我们可以这样理解:
在这里插入图片描述

4.友元

在这里插入图片描述
实现一下:

void operator<<(ostream& out, const Date& d)
{
	out << d._year << " " << d._month << " " << d._day << endl;
}

在这里插入图片描述
此时就可以正常运行了
在这里插入图片描述
在这里插入图片描述
因此我们只需要这样做
在这里插入图片描述
在这里插入图片描述
就可以正常运行了

5.想要连续进行流插入怎么办呢?

下面的问题是<<流插入运算符是支持连续进行的啊
那么我们可不可以也这样呢?
在这里插入图片描述
报错
为什么呢?
因为我们刚才实现的是
在这里插入图片描述
上面的分析过程就有点类似于物理中的整体+隔离法进行受力分析了
下面我们来实现一下:

ostream& operator<<(ostream& out, const Date& d)
{
	out << d._year << " " << d._month << " " << d._day << endl;
	return out;
}

在这里插入图片描述

6.流提取>>运算符重载

类似于流插入<<,流提取也可以按照同样的思路去重载

istream& operator>>(istream& in, Date& d)
{
	in >> d._year >> d._month >> d._day;
	return in;
}

在这里插入图片描述

7.C++为什么要设计流插入和流提取呢?

在这里插入图片描述

九.const成员函数

1.为什么要有const成员函数

在这里插入图片描述
有些情况下,我们需要定义出const修饰的类型的对象
而这时,因为我们的成员函数中的this指针默认是没有const去修饰的
那么这种情况下就会出现这种错误

怎么办呢?
我们无法显式定义this指针,那么就无法直接在this指针的类型前面加上const修饰

于是C++创始人规定:
我们可以这样去给一个成员函数的this指针加上const修饰

void Print() const
{
	cout << _year << " " << _month << " " << _day << endl;
}

就是在函数的()后面加上const
在这里插入图片描述

2.成员函数没有加const的意想不到的错误

在这里插入图片描述
下面就是一个典型的错误示范:
在这里插入图片描述
在这里插入图片描述
而当我们给>运算符函数加上const修饰之后
在这里插入图片描述
正常运行

3.const成员函数定义的原则

在这里插入图片描述

4.几个小问题

在这里插入图片描述
下面我们来验证一下:
第4条
在这里插入图片描述
第3条:
在这里插入图片描述

十.取地址运算符重载

这是最后两个类的默认成员函数

既然是类的默认成员函数,那么就说明:如果我们没有定义取地址运算符重载函数,那么编译器会生成默认的取地址运算符重载函数

它们的具体实现如下:

Date* operator&()
{
	return this;
}
const Date* operator&() const
{
	return this;
}

这两个运算符一般不需要重载,使用编译器生成的即可,只有特殊情况,才需要重载,比如不想让别人得到正确的地址

Date* operator&()
{
	return nullptr;
	//或者直接返回一个错误地址
	return (Date*)0x11223344;
}

在这里插入图片描述

十一.具体的解决方案(日期类的完善)

我们将Date类的声明跟定义分离
结果发现:

1.缺省参数的一个常见错误

在这里插入图片描述
为什么会报错呢?
这个原因我们在学习缺省参数的时候提到过,

也就是:缺省参数的缺省值不能同时在声明和定义中都有
规定要在声明中给出,定义中不要给缺省值
在这里插入图片描述
这样就可以了

2.Date.h

#pragma once
#include <iostream>
using namespace std;
#include <assert.h>
class Date
{
public:
	void Print() const;
	Date(int year = 1, int month = 1, int day = 1);
	Date(const Date& d1);
	Date& operator=(const Date& d);
	bool operator==(const Date& d) const;
	bool operator!=(const Date& d) const;
	bool operator>(const Date& d) const;
	bool operator<=(const Date& d) const;
	bool operator>=(const Date& d) const;
	bool operator<(const Date& d) const;
	//返回year年month月有多少天
	int GetMonthDay(int year, int month);
	//上面这个+复用+=更好
	//拷贝0次
	Date& operator+=(int day);
	//+复用+=
	//拷贝2次
	Date operator+(int day) const;
	//前置++:拷贝0次
	Date& operator++();
	//后置++,为什么是int呢?,C++创始人定的语法,没有为什么
	//拷贝2次
	Date operator++(int);
	//前置++拷贝0次,后置++拷贝2次,因此前置++更好
	Date& operator-=(int day);
	Date operator-(int day) const;
	int operator-(const Date& d) const;

	friend ostream& operator<<(ostream& out, const Date& d);

	friend istream& operator>>(istream& in, Date& d);

	Date* operator&()
	{
		return this;
	}
	const Date* operator&() const
	{
		return this;
	}
private:
	int _year;
	int _month;
	int _day;
};

3.Date.cpp

#include "Date.h"
void Date::Print() const
{
	cout << _year << " " << _month << " " << _day << endl;
}
Date::Date(int year, int month, int day)
{
	//对传入的年份进行检查
	if ((year < 0) || (month < 1 || month>12) || (day<1 || day>GetMonthDay(year, month)))
	{
		//assert(false);//粗暴一点的方法
		Print();//本质是:this->Print();
		cout << "输入的日期为非法日期" << endl;
		exit(-1);
	}
	_year = year;
	_month = month;
	_day = day;
}
Date::Date(const Date& d1)
{
	//对传入的年份进行检查
	if ((d1._year < 0) || (d1._month < 1 || d1._month>12) || (d1._day<1 || d1._day>GetMonthDay(d1._year, d1._month)))
	{
		//assert(false);//粗暴一点的方法
		Print();//本质是:this->Print();
		cout << "拷贝对象的日期为非法日期" << endl;
		cout << "无法进行拷贝" << endl;
		exit(-1);
	}
	_year = d1._year;
	_month = d1._month;
	_day = d1._day;
}
Date& Date::operator=(const Date& d)
{
	//对传入的日期进行检查
	if ((d._year < 0) || (d._month < 1 || d._month>12) || (d._day<1 || d._day>GetMonthDay(d._year, d._month)))
	{
		//assert(false);//粗暴一点的方法
		cout << "拷贝对象的日期为非法日期: ";
		cout << "无法进行拷贝" << endl;
		exit(-1);
	}
	_year = d._year;
	_month = d._month;
	_day = d._day;
	return *this;
}
bool Date::operator==(const Date& d) const
{
	return _year == d._year && _month == d._month && _day == d._day;
}
bool Date::operator!=(const Date& d) const
{
	return ((*this) == d);
}
bool Date::operator>(const Date& d) const
{
	if (_year > d._year)
	{
		return true;
	}
	if (_year == d._year && _month > d._month)
	{
		return true;
	}
	if (_year == d._year && _month == d._month && _day > d._day)
	{
		return true;
	}
	return false;
}
bool Date::operator<=(const Date& d) const
{
	return !((*this) > d);
}
bool Date::operator>=(const Date& d) const
{
	return (*this) > d || (*this) == d;
}
bool Date::operator<(const Date& d) const
{
	return !((*this) >= d);
}

//返回year年month月有多少天
int Date::GetMonthDay(int year, int month)
{
	int MonthArray[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
	//闰年的特殊情况
	if ((month == 2) && ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)))
	{
		MonthArray[month]++;
	}
	return MonthArray[month];
}

//上面这个+复用+=更好
//拷贝0次
Date& Date::operator+=(int day)
{
	//加一个负数==减去这个负数的相反数
	if (day < 0)
	{
		return (*this) -= -day;
	}
	_day += day;
	while (_day > GetMonthDay(_year, _month))
	{
		_day -= GetMonthDay(_year, _month);
		_month++;
		if (_month == 13)
		{
			_month = 1;
			_year++;
		}
	}
	return *this;
}

//+复用+=
//拷贝2次
Date Date::operator+(int day) const
{
	Date d1(*this);
	d1 += day;
	return d1;
}
//前置++:拷贝0次
Date& Date::operator++()
{
	*this += 1;
	return *this;
}
//后置++,为什么是int呢?,C++创始人定的语法,没有为什么
//拷贝2次
Date Date::operator++(int)
{
	Date d(*this);
	//+=已经考虑了不同月份日期不同
	*this += 1;
	return d;
}
//前置++拷贝0次,后置++拷贝2次,因此前置++更好
Date& Date::operator-=(int day)
{
	//减一个负数==加上这个负数的相反数
	if (day < 0)
	{
		return (*this) += -day;
	}
	_day -= day;
	while (_day <= 0)
	{
		_month--;
		if (_month == 0)
		{
			_month = 12;
			_year--;
		}
		_day += GetMonthDay(_year, _month);
	}
	return *this;
}
Date Date::operator-(int day) const
{
	Date d(*this);
	d -= day;
	return d;
}
int Date::operator-(const Date& d) const
{
	Date max = *this;
	Date min = d;
	int flag = 1;//假设:*this大于d,左大于右     左-右
	//假设错误
	if (min > max)
	{
		max = d;
		min = *this;
		flag = -1;//右大于左
	}
	int day = 0;
	while (min < max)
	{
		//++min;//++min是代码实现是调用+=的,那我直接调用+=不好吗,更快
		min += 1;
		day++;
	}
	return flag * day;
}
ostream& operator<<(ostream& out, const Date& d)
{
	out << d._year << " " << d._month << " " << d._day << endl;
	return out;
}
istream& operator>>(istream& in, Date& d)
{
	in >> d._year >> d._month >> d._day;
	return in;
}

以上就是
C++类和对象中:运算符重载+const成员函数+日期类的完善
的全部内容,
希望能对大家有所帮助!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1158381.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

小米6x 小米6x 一键刷twrp 安装面具magisk 实现root

为了在安卓上抓包chatgpt 只能root小米6x 来安装抓包工具小黄鸟 才能安装根证书来解析https。 工具面具卡包 1.一键刷TWRP 小米6X一键线recovery(需要先解bl锁)-20180506重发.zip - 蓝奏云 2. 在安装前清除数据 在开始刷机之前&#xff0c;需要先格式化 Data 分区和清除 Cac…

机械应用笔记

1. 螺纹转换头&#xff1a;又名金属塞头&#xff0c;例如M20-M16&#xff1b;适合于大小螺纹转换用&#xff1b; 2. 螺纹分英制和公制&#xff0c;攻丝同样也有英制和公制之分&#xff1b; 3. DB9头制作&#xff0c;M6.5的线&#xff0c;用M6.5的钻头扩线孔&#xff0c;在根…

onlyoffice 二次开发 连接器(connector) 表单填(Filling out the form) jsApi级别操作文档

阅读须知&#xff1a;本文针对有对word/excel进行js操作的需求 本次改造基于V7.3.3进行&#xff0c;已经更新进入docker。 小伙伴们须知&#xff1a;改造后的office docker需要付费&#xff08;875元&#xff09;&#xff0c;等于wps一个月费用 欢迎大家一起交流&#xff1a;V&…

Pandas教程(非常详细)(第一部分)

Pandas 库是一个免费、开源的第三方 Python 库&#xff0c;是 Python 数据分析必不可少的工具之一&#xff0c;它为 Python 数据分析提供了高性能&#xff0c;且易于使用的数据结构&#xff0c;即 Series 和 DataFrame。Pandas 自诞生后被应用于众多的领域&#xff0c;比如金融…

HarmonyOS鸿蒙原生应用开发设计- 元服务(原子化服务)图标

HarmonyOS设计文档中&#xff0c;为大家提供了独特的元服务图标&#xff0c;开发者可以根据需要直接引用。 开发者直接使用官方提供的元服务图标内容&#xff0c;既可以符合HarmonyOS原生应用的开发上架运营规范&#xff0c;又可以防止使用别人的元服务图标侵权意外情况等&…

echarts 实现分组和显示总数

echart 除了能显示坐标轴外&#xff0c;还可以对坐标轴进行分组&#xff0c;可以更直观的观察数 据&#xff0c;本博文记录一下关于分组的探索&#xff0c;先展示一下效果图&#xff1a; // 直接复制到echarts官方示例中查看效果 let xAxisData []; let data1 []; let data2 …

我的风信共享

登录界面&#xff1a; 管理员可以对所有文章包括管理员发布的文章进行管理&#xff1a; 普通用户只能对自己的文章进行操作; 分类展示&#xff1a; 文章发布功能&#xff1a; 搜索功能&#xff1a;

配电房/变电站可视化智能监控系统方案,助力打造“无人值守”监管模式

一、背景需求分析 配电房管理工作一直是供电系统运行管理可靠性的薄弱环节之一&#xff0c;一些配电房开关跳闸和配电房环境过热影响设备运行、配电房水浸导致设备损坏、配电房设备被盗等&#xff0c;既容易毁坏设备&#xff0c;也容易影响正常用电情况。传统监管方式是通过工…

Nacos 注册中心介绍与实操

前言 本文为个人SpringCloud学习笔记&#xff0c;主要记录Nacos的注册中心实操、SpringBoot多模块编程实操等 注册中心 注册中心介绍 注册中心是微服务的一个重要组件&#xff0c;用于实现服务的注册与发现&#xff0c;主要作用包括以下&#xff1a; 服务注册&#xff1a;…

【从零开始学习Linux】一文带你了解Shell外壳及用户权限(一)

&#x1f6a9;纸上得来终觉浅&#xff0c; 绝知此事要躬行。 &#x1f31f;主页&#xff1a;June-Frost &#x1f680;专栏&#xff1a;Linux入门 &#x1f52d;【从零开始学习Linux】系列均属于Linux入门&#xff0c;主要包含Linux操作系统下的指令、操作、权限以及开发工具&a…

k8s之集群调度

目录 调度 工作机制 调度过程 调度算法 优先级 指定调度节点 调度 Kubernetes 是通过 List-Watch 的机制进行每个组件的协作&#xff0c;保持数据同步的&#xff0c;每个组件之间的设计实现了解耦。 用户是通过 kubectl 根据配置文件&#xff0c;向 APIServer 发送命令…

SandboxAQ推出量子安全“守卫军”!开源框架和加密算法元库Sandwich

Sandwich示意图&#xff08;图片来源&#xff1a;网络&#xff09; 未来几年&#xff0c;IT领域需要对当今计算机通信网络中使用的密码学协议进行一些重大更改&#xff0c;以确保它们仍然安全&#xff0c;且对未来的量子计算机具有应对能力。 其中一个关键特征称为加密敏捷性…

黑芝麻智能助力亿咖通·天穹Pro行泊一体智能驾驶计算平台正式量产

11月1日&#xff0c;亿咖通科技旗下首款智能驾驶计算平台——亿咖通天穹Pro行泊一体智能驾驶平台已向客户开始正式量产交付。亿咖通天穹行泊一体智能驾驶计算平台是亿咖通科技基于黑芝麻智能华山二号A1000&#xff0c;由双方合作研发并推出的具备行泊一体能力的智能驾驶解决方案…

【Linux进程】先谈硬件—冯诺依曼体系结构

目录 冯诺依曼体系 冯诺依曼体系结构 冯诺依曼体系的工作流程 为什么一个程序要运行&#xff0c;必须的先加载到内存中运行? 从软件数据流角度理解冯诺依曼 冯诺依曼体系结构 我们常见的计算机&#xff0c;如笔记本。我们不常见的计算机&#xff0c;如服务器&#xff0c…

信息收集-web架构-源码

一、web架构资产-平台指纹识别-源码 代码审计&#xff0c;从代码中挖掘漏洞&#xff0c;有代码才能做的&#xff0c;没有代码&#xff08;黑盒&#xff09;&#xff0c;有代码&#xff08;白盒&#xff09; 没有源码只能做黑盒&#xff0c;有源码黑白盒都可做 有源码成功率高…

学习笔记二十七:K8S控制器Statefulset入门到企业实战应用

这里写目录标题 Statefulset控制器&#xff1a;概念、原理解读Statefulset资源清单文件编写技巧查看定义Statefulset资源需要的字段查看statefulset.spec字段如何定义&#xff1f;查看statefulset的spec.template字段如何定义 Statefulset使用案例&#xff1a;部署web站点State…

Java 实现灰度图转真彩图

目录 1 问题2 实现1 问题 Java 实现灰度图转真彩图 将以上的图片,jpg png 都可以,转为有颜色的 2 实现 import javax.imageio.ImageIO; import java.awt.*; import java.awt.image.BufferedImage; import java.awt.image.Raster; import java.io.File;public class DatUtil…

k8s之pod进阶---资源限制与探针

目录 一、资源限制 二、探针&#xff08;健康检查&#xff09; 2.1 含义 2.2 探针的三种规则 2.3 probe支持三种检查方法 2.4 探针的示例 1、存活探针&#xff1a;livenessProbe &#xff08;1&#xff09;exec方式 &#xff08;2&#xff09;httpGet方式 &#xff08;…

百度 | 文心一言也开始收费了

好久没用文心一言了 之前一直用ChatGPT的 今天打开文心一言一看&#xff0c;好家伙 出了文心大模型4.0&#xff0c;想体验一下来着 可惜是收费的 看下价格&#xff0c;没买 50块钱一个月&#xff0c;对比ChatGPT4来说&#xff0c;确实不算贵 毕竟gpt4一个月20美刀 &#xff0c;…

CEYEE希亦新品洗地机Pro系列发布, 领跑行业的「水汽混动」技术的旗舰新杰作

CEYEE希亦全新一代洗地机T800 PRO正式上市&#xff0c;采用双滚刷&#xff0c;双倍活水洗拖洗方式&#xff0c;达到拖一遍抵两遍&#xff0c;相对于10倍洁净效果&#xff01; 这款希亦Pro系列产品不仅刷新了洗地机行业技术水准&#xff0c;满足了用户愈发极致的清洁效能追求&a…