
大家好啊,我是云泽Q,一名热爱计算机技术的在校大学生。近几年人工智能技术飞速发展,为了帮助大家更好地抓住这波浪潮,在开始正文之前,我想先为大家推荐一个非常优质的人工智能学习网站)。它提供了从基础到前沿的系列课程和实战项目,非常适合想要系统入门和提升AI技术的朋友,相信能对你的学习之路有所帮助。
C++期望有更便捷,可读性更高的方式去使用运算符,这里有一个限制,内置类型可以尝试使用各种运算符,但是自定义类型不可以,编译器也不知道自定义类型如何使用,和拷贝构造是一样的,内置类型都是自己定义好的一些简单的类型(例如加减乘除),要拷贝时,底层有对应的指令支持,一个指令就拷贝了 。但是自定义类型是一个复合类型,其底层可能有各式各样的各种结构,程序员怎么运算,编译器不知道,编译器就无法支持。而且这个运算符到底有没有意义,也是另外一回事了,这是什么意思呢?
比如说,两个日期类相加就没有意义,两个日期类相减就可以算一个天数了。这个运算符具体在什么场景下到底有没有意义,怎么来算都要程序员来定,所以基于这样的原因,自定义类型是不支持使用各种运算符的

这里想要支持的话,C++之父也设置了一套机制


但是没有显式调用的必要
int main()
{
int i = 0, j = 1;//内置类型就转换成对应的指令
bool ret = i == j;如果这里不接收i==j的结果,比较没有意义,此时转到反汇编指令就会被优化

这里对应的指令是cmp,cmp就是compare,将i给eax,eax就是i的值,然后和j进行一个cmp比较,后面指令的意思就是取到比较的结果

这里也确实看到对于自定义类型要转换为调用对应的函数
这里的代码还有很多缺点,都知道传值传参是要调用拷贝构造的,对于内置类型,用传值传参和引用传参区别不大,第一内置类型都比较小,传值传参拷贝代价也不大,第二它也不涉及调用拷贝构造,都是指令直接完成。但是对于自定义类型,尤其是深拷贝的类型,用引用传参就非常的重要,而且这里函数不改变参数,加上const更好,这样普通对象,const对象,都可以调用

这样为了能比较将内置成员改为公有就太挫了,这里还有另外三种解决方式,其中一种我在类和对象下一篇来讲,我这篇写两种 有些地方会习惯去提供函数,这种方式Java很喜欢用
直接访问私有不行,但是可以提供一个公有的成员函数,对应位置就可以获取该成员,如图就可以提供Get Year,Get Month,Get Day

C++更习惯在类中实现运算,但是放到类中就语法报错了(运行报错operator==的参数太多了),这是因为类中的成员函数的参数列表还有一个隐含的this指针,所以参数还要进行调整

调用的时候编译器也很灵活,它看到运算符重载会找对应的函数,全局和类中它都会去找,严格来说先去类中找,找到之后就会转换为对应的调用,这里 d1 是调用这个函数的对象(相当于函数的第一个参数,即当前对象),d2 是传递给函数的参数(第二个操作数)。 这里还是建议使用operator传参,养成好习惯

之前C/C++是这样调用函数指针的
通过成员函数指针调用成员函数是下面这样调用的,之前那样调用就不可以了,有类域的限制
之所以这样写是因为成员函数的函数指针和普通函数的函数指针是不一样的,普通函数的func1是无参的,但是成员函数func2是有参的(隐含的this指针)

Date operator+(const Date& d, int x);赋值运算符重载(有些书上也叫赋值拷贝)是一个默认成员函数,用于完成两个已经存在的对象直接的拷贝赋值,这里要注意跟拷贝构造区分,拷贝构造用于一个对象拷贝初始化给另一个要创建的对象

这里调试就是完全没有问题的
赋值运算符重载的特点:

return也没有使用传值传参的必要,* this出了作用域还在,d3的生命周期在main中,换用引用传参就不会调用拷贝构造直接返回了

这里先d5赋值给d3,d3作为赋值运算符重载的返回值赋值给d1,这里有两个函数调用
赋值运算符重载还有一个很极端的场景
d1 = d1;//语法上是允许的,但逻辑就白白执行了所以语法上可以给个判断,如果自己给自己赋值,就不再进行拷贝
Date& operator=(const Date& d)//日期类的拷贝就是简单的值拷贝
{
//右边不是引用,是取地址
if (this != &d)//如果左对象的地址和右对象的地址是一样的,就不进行拷贝
{
_year = d._year;
_month = d._month;
_day = d._day;
}
return *this;
}我这里实现一个阉割版的日期类,日期类加一个天数就有一个问题了,加个小天数还可以,加个大天数还需要进位

首先拿8月来举例,进位之前首先要获取8月的天数,8月31天,减掉之后,月数进一位,此时的日期还不合法,继续进位,获取9月天数并减掉,以此类推,直至日期合法
代码实现 Date.h
#pragma once
#include<iostream>
#include<assert.h>
using namespace std;
//日期类的拷贝构造,赋值重载,析构都不需要实现,默认的够用
class Date
{
public:
Date(int year = 1900, int month = 1, int day = 1);
void Print();
//高频调用的小函数最好搞成内联
//这里在类里面定义的函数直接就是内联
int GetMonthDay(int year, int month)
{
//不写成静态的话每次都要在栈帧创建数组
static int monthDayArray[13] = { -1,31,28,31,30,31,30,
31,31,30,31,30,31 };//-1的作用是对其月数与下标
if (month == 2 && (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0))
{
//是闰年2月返回29天
return 29;
}
else
{
return monthDayArray[month];
}
}
bool operator<(const Date& d);
bool operator<=(const Date& d);
bool operator>(const Date& d);
bool operator>=(const Date& d);
bool operator==(const Date& d);
bool operator!=(const Date& d);
// d1 += 天数
Date& operator+=(int day);
// d1+ 天数
Date operator+(int day);
// d1 -= 天数
Date& operator-=(int day);
Date operator-(int day);
// d1 - d2
int operator-(const Date& d);
//++d1 -> d1.operator++()
Date& operator++();
// d1++ -> d1.operator++(0)
// 为了区分,构成重载,给后置++,强行增加一个int形参
// 这里不需要写形参名,因为接受之是多少不重要,也不需要用
// 这个参数仅仅是为了跟前置++构成重载区分
Date operator++(int);
Date& operator--();
Date operator--(int);
private:
int _year;
int _month;
int _day;
};Date.cpp
#define _CRT_SECURE_NO_WARNINGS
#include"Date.h"
//缺省参数声明和定义不能同时给值
Date::Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
void Date::Print()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
// d1 += 天数
Date& Date::operator+=(int day)
{
_day += day;
while (_day > GetMonthDay(_year, _month))
{
//不合法要进位,把当前月的天数减掉
_day -= GetMonthDay(_year, _month);
++_month;
if (_month == 13)
{
++_year;
_month = 1;
}
}
//指向调用该函数的对象,不是局部对象
return *this;
}test.cpp

这里d1也改变了,是一个加等的逻辑,下面的逻辑是d1不变,只返回d1+100后的值的逻辑
Date.cpp
// d1 + 天数
Date Date::operator+(int day)
{
//*this是d1,d1不能改变,所以用d1拷贝了tmp,改变tmp,返回tmp即可
Date tmp(*this);//拷贝构造一个对象出来
tmp._day += day;
while (tmp._day > GetMonthDay(tmp._year, tmp._month))
{
//不合法要进位,把当前月的天数减掉
tmp._day -= GetMonthDay(tmp._year, tmp._month);
++tmp._month;
if (tmp._month == 13)
{
++tmp._year;
tmp._month = 1;
}
}
//d3 + 100 生成一个 “新的日期对象”(100 天后的日期),而 d3 本身保持不变
//所以使用值返回而不是引用返回
return tmp;
}
注意,这里d3赋值给d4是拷贝构造,用 d3 + 100 的结果创建并初始化 d4 但是当前的函数依旧实现的不好,他们逻辑是类似的,下面将他们复用了

在 C++ 中,tmp += day; 能够间接调用 operator+=,是因为 += 是可以被重载的运算符,并且对于用户自定义的类(如这里的 Date 类),当我们为该类重载了 operator+= 运算符后,就可以像使用内置类型(如 int、double 等)的 += 操作一样,对该类的对象使用 += 操作,此时就会调用我们重载的 operator+= 函数。所以当执行 tmp += day; 时,编译器会识别到这是对 Date 对象的 += 操作,进而调用我们重载的 operator+= 函数,实现对 tmp 这个 Date 对象增加 day 天的功能

效果是一样的
反向也可以复用,+由自己实现,+=复用+

*this + day 会调用 operator+,返回一个临时的 Date 对象(tmp的拷贝),*this = … 是将这个临时对象赋值给当前对象(*this),编译器会调用 Date 类的默认赋值运算符重载(operator=)

这两种复用方式前者更好一些,比如说有个d1对象,调用+100,有个d2对象,调用一下+=100,分别调用两种复用
d2调用+=100,+=内部无拷贝,d2传给this,一系列操作后传引用返回

+内部有拷贝,拷贝构造就是一次,传值返回是一次拷贝,复用+=无拷贝,第一种复用一共两种拷贝
第二种复用,+有一次拷贝构造,传值返回有一次拷贝。+=中复用+有两次拷贝,*this = *this + day;再调一次赋值也是一次拷贝,第二种复用方式有5次拷贝
所以让拷贝多的去复用拷贝少的最好
这里再说一下前置++和后置++,二者返回值不同,后置是++之前的,前置是++之后的。由于无论是哪种++都是单操作数的,单操作数并没有规定操作数是在左边还是右边,只传给一个参数,不像两个参数有顺序,二者的函数名都是operator++,都是一个参数。二者最大的问题是如何区分
两个函数名相同的函数想要同时存在必须构成重载,构成函数重载就要求函数名相同,参数不同,由于前置++用的相对多一些(前置无拷贝,效率高),所以编译器做了特殊处理,不接收返回值的情况下,前置++效率高一些, 所以默认情况下前置++不加参数,(后置++还有两个拷贝)后置++为了与之区分加了一个额外的参数,这个参数无实际意义,传什么都行()所以也不用写形参名字,只是为了与前置++构成重载




虽然这里写了赋值符号,但依旧是拷贝构造
前面可以看出+=之中因为没有拷贝构造效率更高,所以复合的时候也是+里复合使用+=,接下来继续实现-和-=,依旧在-中复合使用-=,这里先减掉50天,+是进位,-就是借位,下面的借位是问8月借,9月的8天已经减完了,8月是31天,然后以此类推,借到合法为止



接下来写-


上面程序还有一个小bug,参数int可以传正也可以传负,在内部还要进行一个简单的处理,+和-复用的是+=和-=



无论是前置还是后置,编译器编译之后都会转化为d1.operator–();

但是二者返回值是不一样的,减减d1返回的是减减之后的值,d1减减返回的是减减之前的值

调用的时候区分方式和前置与后置++区分方式一样,这增加的一个int类型仅仅是为了构成重载

对于类优先建议调用前置,前置效率更高,后置会多两次拷贝

显式调用



这里的书写顺序就建议
这样的写法有两种好处,减少了代码量,更加美观。在日后如果要增加功能的时候,例如说加时分秒,只用要在最初的等于和小于把时分秒的逻辑放进来,其他的代码都是前两个函数的复用,不用改,达到了降本增效的目的。
这也是日常处理业务的一个小技巧,尽可能代码复用性更高,就可以大大的增加可维护性
这里提供两种思路
第一种思路,让两个日期都去计算离当年的一月一日差多少天

算出两个天数x和y之后,再让2025 1 1与2020 1 1相减,得出他们之间差了5年(z),再加上2020年到2025年中间经过了几个闰年,闰年的天数是366,例如这里2020和2024年是闰年,再加上(x-y) 例如:5年×365+2+(x-y)
还有一个更简单的思路,这个相对于就比较暴力了,两个日期相减必定有一个日期大,另一个日期小,可以先比较一下,算出小的那个日期,然后让小的日期循环向大的日期++,每次加一天,直到和大的日期相等,加了多少次,就相差多少天
如果两个日期差的很多,比如差了几百年的情况,第一种思路效率更高,因为年的跨度一把就算出来了,第二种复用度更高 我这里只展示第二种思路,这其中也有很多小技巧


这里flag是一个纠正的作用,如果第一个日期大,第二个日期小,那么加出来的结果就是正的,加的天数×1就没问题。但是如果第一个小,第二个大,就要×负一变为负的天数
补充一下,对于这种类定义的自定义类型想要用运算符是需要自己重载的,而且日期类默认是不能进行流插入流提取的输入输出的

因为这个日期类是自己定义的,以什么样的格式输出编译器是不知道的,所以这里想输出就要重载运算符<<(流插入)
这里重载为成员函数的对象一个为日期类对象d1,还有一个对象是cout,cout是一个ostream类型的对象,ostream是库里面实现的一个类

点击ostream


内置类型(int,char…)可以直接使用的原因是库里面已经重载了它们对应的实现,ostream特殊一点,它的输出输入复杂一些,不能转换为对应的指令,没有对应的指令,所以会转换为调用对应的函数,转换为调用库里面的operator<<,cout能自动识别类型源于这样的函数重载
int i = 1;
double d = 1.1;
//函数重载
cout << i;//这里能自动识别类型相当于转换为cout.operator<<(i)这样的函数调用
cout << d;//这里能自动识别类型相当于转换为cout.operator<<(d)这样的函数调用
return 0;下面就重载一下对应的运算符,使得自定义类型能够支持这样输出的流插入 一个是日期类对象,一个是ostream类型的对象,这里先直接写到Date.h类里面

cout传给了ostream,out是cout的别名,日期类传给了隐含的this
而且这样的多个函数调用实际效率并不会低,如果是个小函数,高频调用的东西,C++会将其搞成内联,内联会将调用的地方展开,就没有函数调用的开销了,当然能否真的称为内联由编译器决定
另一方面现在的计算机运行计算速度已经很快了,这种多个函数调用也不会有太大的性能影响

但是这样这样输出的写法就看的很奇怪
cou << i流插入输出的运算符表达的意思是一个对象或变量流向控制台去,图片中的写法就变为流对象流向到日期类对象里面去
虽然代码可以运行,但是不符合可读性,这里就想办法让cout占据第一个参数,一个方法就是像库里面一样重载,库里面是写为ostream这个类的成员函数,如果能向ostream类里面增加一个成员函数,这个函数的第一个形参是ostream的this指针,就可以让cout占据第一个参数,但是库是无法随便更改的

另一个方法就是重载运算符的这个函数不把它放在类中,这样其就不是一个成员函数,就没有隐含的this指针了,将其放为成员函数的核心原因还是因为想访问私有的成员变量
为了可读性,流插入输出运算符的重载都是写为全局的函数
接下来为了访问私有成员有几种解决方案,第一种就是让日期类提供一个成员函数间接访问(GetYear,GetMonth,GetDay),还有一种方式更简单,就是友元,就在类中的函数前新加一个关键字friend我在下一篇文章详细去写
Date.h


Date.cpp

想让类外面的一个函数,用日期类的对象访问私有成员变量,就将这个函数定义为该类的友元,友元声明放到哪里都可以(公有,保护),友元函数并不是这个类正真的成员,不存在私有或要用访问限定符的概念,它只是一个声明,并不是类里定义的东西,但一般放在开始居多。 可以理解为告诉这个类,我是你的朋友,我可以访问你的私有

但是当前的代码是不支持连续的输出的

连续输出是从左到右,cout<<d1就是一个函数调用,调用流插入函数,所以需要一个返回值作为下一次输出调用的左操作数,返回值就需要一个cout


前面在.h文件里的两个声明也要加
也是同样的道理,cin是istream类型的对象

内置类型实现流提取也已经实现好了

用的时候就调用库里面对应的函数来达到一个自动识别的效果




具体的流插入流提取的内部的细节是根据自己的需求来写的,这里日期类就单纯的输入输出比较简单
这里说一下为什么 ostream 的 operator<<第二个参数是 const,而 istream 的 operator>> 第二个参数没有 const
可以看出当前的程序还有一些缺陷,如果输入了一些非法的日期,也是能正常输出的和拿去运算的

再写一个日期类的检查
也可以在输入的地方进行一个更严格的检查,直接不让输入非法日期




Date d1(2025, 9, 8);
d1.Print();之前这样是能正常打印的,但是如果在Date前加一个const就不行了

这里就涉及一个权限的放大,对象调用成员函数时,会把对象的地址传递给隐含的this指针,this指针默认的类型是Date* ,但是这里对d1取地址类型是const Date* ,指向的内容不能修改的指针,很多情况下编译器的报错时的描述是不太准确的
虽然this指针有一个const,但是它的const在星的右边(Date* const this),星的右边是修饰this指针本身不能被改变,成员函数里面不能去修改this指针。这里要让Print函数的this指针也变为const Date* ,
这里的难点就是this指针是隐含的形参,他是编译器自动生成的形参,基于这样的问题就有了接下来的const成员函数

注意,声明和定义分离的话两边都要加
当在函数的参数列表右边加const,就修饰的是this指针指向的内容了,对象d1被const修饰,自己不能改变,传递给this指针,这个this指针无论是本身还是其指向的内容都不能被改变

用const对象调用Print之后,后面用普通对象也可以调用Print了,这就是权限不能放大,但是权限可以平移和缩小

要修改成员变量的成员函数不能加const,但是不修改成员变量的成员函数都应该加上const
虽然函数在初始化和析构的时候调用构造函数的时候传了一个this指针过去,但构造和析构函数肯定是没有const的,要特殊说明一下,所有const修饰的对象在初始化和析构的时候都做了一个特殊处理,在初始化和析构时没有const属性的,否则const对象无法初始化和析构了
const int i = 0;上面代码如果在初始化的时候就有const属性,就无法初始化了,在初始化之后就有const属性了
而且不修改成员变量的函数加上const之后,普通对象和const对象都可以调用这个成员函数
取地址运算符重载分为普通取地址运算符重载和const取地址运算符重载,一般这两个函数编译器自动生成的就够用了,不需要去显式实现,除非一些很特殊的场景,比如我不想让别人取到当前类对象的地址,就可以自己实现一份,胡乱返回一个地址


这里之所以还有一个普通取地址运算符重载的原因是,仅管无论普通对象还是const都可以调用取地址运算符函数,但是使用取地址运算符的时候,如果是个const对象,其返回值是const Date* ,但是普通对象返回值应该是Date* ,二者同时写返回值更加匹配,而且构成函数重载
Date.h
#pragma once
#include<iostream>
#include<assert.h>
using namespace std;
class Date
{
// 友元函数声明
friend ostream& operator<<(ostream& out, const Date& d);
friend istream& operator>>(istream& in, Date& d);
public:
Date(int year = 1900, int month = 1, int day = 1);
void Print() const;
int GetMonthDay(int year, int month) const
{
static int monthDayArray[13] = { -1,31,28,31,30,31,30,31,31,30,31,30,31 };
if (month == 2 && (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0))
{
return 29;
}
else {
return monthDayArray[month];
}
}
bool CheckDate() const
{
if (_month < 1 || _month>12)
return false;
if (_day<1 || _day>GetMonthDay(_year, _month))
return false;
return true;
}
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;
//d1 += 天数
Date& operator+=(int day);
//d1 + 天数
Date operator+(int day) const;
//d1 -= 天数
Date& operator-=(int day);
//d1 - 天数
Date operator-(int day) const;
//++d1 -> d1.operator++()
Date& operator++();
//d1++ -> d1.operator++(0)
//为了区分,构成重载,给后置++,强行增加一个int形参
//这里不需要写形参名,因为接受值是多少不重要,也不需要用
//这个参数仅仅是为了根前置++构成重载区分
Date operator++(int);//(int i)
//--d1 -> d1.operator--()
Date& operator--();
//d1-- -> d1.operator--(0)
Date operator--(int);
// d1 - d2
int operator-(const Date& d) const;
//d1.operator<<(cout);
//void operator<<(ostream& out)
//{
// //后面都属于内置类型
// out << _year << "/" << _month << "/" << _day << '\n';
// //out << _year内部的调用是有返回值的,也是一个ostream类型的对象,也就是把cout返回了,这里是把cout的别名返回了
// //所以这里可以理解为多个函数调用,和连续赋值一样的道理,只是这里是从左到右
// //out << _year又作为out << _year << "/"的左操作数去调用
// //out << _year << "/" 作为out << _year << "/" << _month的左操作数再去调用
// //这里就是多个流插入调用
//}
Date* operator&()
{
return this;
//return nullptr;
}
const Date* operator&()const
{
return this;
//return nullptr;
}
private:
int _year;
int _month;
int _day;
};
ostream& operator<<(ostream& out, const Date& d);
istream& operator>>(istream& in, Date& d);Date.cpp
#define _CRT_SECURE_NO_WARNINGS
#include"Date.h"
Date::Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
if (!CheckDate())
{
cout << "非法日期:>" << *this;
}
}
void Date::Print() const
{
cout << _year << "-" << _month << "-" << _day << endl;
}
Date& Date::operator+=(int day)
{
if (day < 0)
{
// d1 += -100,直接转换成去调用减等(d1 -= 100)
return *this -= -day;
}
_day += day;
while (_day > GetMonthDay(_year, _month))
{
_day -= GetMonthDay(_year, _month);
++_month;
if (_month == 13)
{
++_year;
_month = 1;
}
}
return *this;
}
//// d1 + 天数
//Date Date::operator+(int day)
//{
// //*this是d1,d1不能改变,所以用d1拷贝了tmp,改变tmp,返回tmp即可
// Date tmp(*this);//拷贝构造一个对象出来
// tmp._day += day;
// while (tmp._day > GetMonthDay(tmp._year, tmp._month))
// {
// //不合法要进位,把当前月的天数减掉
// tmp._day -= GetMonthDay(tmp._year, tmp._month);
// ++tmp._month;
// if (tmp._month == 13)
// {
// ++tmp._year;
// tmp._month = 1;
// }
// }
// //d3 + 100 生成一个 “新的日期对象”(100 天后的日期),而 d3 本身保持不变
// //所以使用值返回而不是引用返回
// return tmp;
//}
//d1 + 天数
Date Date::operator+(int day) const
{
Date tmp(*this);
tmp += day;
return tmp;
}
// d1 -= 100
Date& Date::operator-=(int day)
{
if (day < 0)
{
// d1 -= -100
return *this += -day;
}
_day -= day;
//>0才合法
while (_day <= 0)
{
--_month;
//借上个月的,但是要防止1月减减变0月
if (_month == 0)
{
--_year;
_month = 12;
}
_day += GetMonthDay(_year, _month);
}
return *this;
}
// d1 - 100
Date Date::operator-(int day) const
{
Date tmp = *this; //拷贝构造
tmp -= day;
return tmp; //局部对象
}
//++d1 -> 编译器转换为调用d1.operator++();
Date& Date::operator++()
{
*this += 1;//调用+=赋值运算符重载
return *this;//出了作用域d1还在,用引用返回
}
//d1++ -> 编译器转换为调用d1.operator++(0);括号内数字任意
Date Date::operator++(int)
{
Date tmp(*this);//调用拷贝构造
*this += 1;//调用+=赋值运算符重载
return tmp;//返回++之前的值
//返回的对象是局部对象,不能用引用返回
}
Date& Date::operator--()
{
*this -= 1;
return *this;
}
Date Date::operator--(int)
{
Date tmp = *this;//拷贝
*this -= 1;
return tmp;//拷贝
}
//日期比较小于
bool Date::operator<(const Date& d) const
{
if (_year < d._year)
{
return true;
}
else if (_year == d._year)
{
if (_month < d._month)
{
return true;
}
else if (_month == d._month)
return _day < d._day;
}
//为真的情况都走完了,剩下的就是false
return false;
}
// d1 <= d2
bool Date::operator<=(const Date& d) const
{
//这里复用,小于等于就是小于或等于
return *this < d || *this == d;
}
bool Date::operator>(const Date& d) const
{
//小于等于取反
return !(*this <= d);
}
bool Date::operator>=(const Date& d) const
{
//小于取反
return !(*this < d);
}
//日期比较相等
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);
}
// d1 - d2
int Date::operator-(const Date& d) const
{
//假设第一个日期大
Date max = *this;
Date min = d;
int flag = 1;
if (*this < d)
{
min = *this;
max = d;
flag = -1;
}
int day = 0;
//还可以用小于,不等于的比较逻辑更简单一点
while (min != max)
{
//尽可能用前置++,前置无拷贝
++min;
++day;
}
return day * flag;
}
ostream& operator<<(ostream& out, const Date& d)
{
out << d._year << "/" << d._month << "/" << d._day << '\n';
//出了作用域out还在,用引用返回
return out;
}
istream& operator>>(istream& in, Date& d)
{
while (1)
{
cout << "请依次输入年月日:>";
in >> d._year >> d._month >> d._day;
if (d.CheckDate())
{
break;
}
else {
cout << "输入日期非法,请重新输入" << endl;
}
}
return in;
}Test.cpp
#define _CRT_SECURE_NO_WARNINGS
#include"Date.h"
//class Stack
//{
//public:
// Stack(int n)
// {}
//};
//
//class Myqueue
//{
//public:
// // 初始化列表,后面讲
// Myqueue()
// :_pushst(4)
// ,_popst(4)
// {}
//private:
// Stack _pushst;
// Stack _popst;
//};
//
//int main()
//{
// Myqueue q;
//
// return 0;
//}
//class Date
//{
//public:
// Date(int year = 1, int month = 1, int day = 1)
// {
// _year = year;
// _month = month;
// _day = day;
// }
//
// // Date d4(d3);
// //Date(const Date& d)
// //{
// // _year = d._year;
// // _month = d._month;
// // _day = d._day;
// //}
//
// void Print()
// {
// cout << _year << "-" << _month << "-" << _day << endl;
// }
//private:
// int _year;
// int _month;
// int _day;
//};
//
//// 自定义类型,传值传参要调用拷贝构造
//// void Func(const Date& d)
//void Func(Date d)
//{}
//
//typedef int STDataType;
//class Stack
//{
//public:
// Stack(int n = 4)
// {
// _a = (STDataType*)malloc(sizeof(STDataType) * n);
// if (nullptr == _a)
// {
// perror("malloc申请空间失败");
// return;
// }
// _capacity = n;
// _top = 0;
// }
//
// // Stack st2(st1);
// /*Stack(const Stack& s)
// {
// _a = s._a;
// _capacity = s._capacity;
// _top = s._top;
// }*/
//
// Stack(const Stack& s)
// {
// _a = (STDataType*)malloc(sizeof(STDataType) * s._capacity);
// if (_a == NULL)
// {
// perror("realloc fail");
// return;
// }
//
// memcpy(_a, s._a, s._top * sizeof(STDataType));
//
// _capacity = s._capacity;
// _top = s._top;
// }
//
// void Push(STDataType x)
// {
// if (_top == _capacity)
// {
// int newcapacity = _capacity * 2;
// STDataType* tmp = (STDataType*)realloc(_a, newcapacity *
// sizeof(STDataType));
// if (tmp == NULL)
// {
// perror("realloc fail");
// return;
// }
// _a = tmp;
// _capacity = newcapacity;
// }
// _a[_top++] = x;
// }
//
// ~Stack()
// {
// cout << "~Stack()" << endl;
// free(_a);
// _a = nullptr;
// _top = _capacity = 0;
// }
//private:
// STDataType* _a;
// size_t _capacity;
// size_t _top;
//};
//
//class MyQueue
//{
//private:
// Stack _pushst;
// Stack _popst;
//};
//int main()
//{
// Date d1(2025, 8, 1);
// // 拷贝构造
// Date d2(d1);
//
// const Date d3(2025, 8, 1);
// Date d4(d3);
//
// Func(d1);
//
// ////////////////////////////////////
// Stack st1;
// st1.Push(1);
// st1.Push(2);
// st1.Push(3);
//
// Stack st2(st1);
//
// MyQueue q1;
// MyQueue q2(q1);
//
// return 0;
//}
//int& func2()
//{
// int x = 1;
// return x;
//}
//
//Stack& Func3()
//{
// Stack st;
// return st;
//}
//
//// 10:30
//int main()
//{
// int ret1 = func2();
// cout << ret1 << endl;
//
// Stack ret2 = Func3();
// // Stack ret2(Func3());
//
// Stack st3;
// // 以下都是调用拷贝构造
// Stack st4(st3);
// Stack st5 = st3;
//
// return 0;
//}
//////////////////////////////////////////////////////////////////////////////////
// 运算符重载
//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;
// }
//
// int GetYear()
// {
// return _year;
// }
//
//private:
// 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 operator-(const Date& x1, const Date& x2)
//{
// return 0;
//}
//
//int main()
//{
// int i = 0, j = 1;
// Date d1(2025, 8, 1);
// Date d2(2025, 10, 1);
// bool ret = i == j;
// cout << (d1 == d2) << endl;
// operator==(d1, d2);
//
// d1 - d2;
// operator-(d1, d2);
//
// return 0;
//}
//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;
// }
//
// 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(2025, 8, 1);
// Date d2(2025, 10, 1);
//
// cout << (d1 == d2) << endl;
// d1.operator==(d2);
//
// return 0;
//}
//void func1()
//{
// cout << "void func()" << endl;
//}
//
//class A
//{
//public:
// void func2()
// {
// cout << "A::func()" << endl;
// }
//};
//int main()
//{
// // 普通函数指针
// void(*pf1)() = func1;
// (*pf1)();
//
// // A类型成员函数的指针
// void(A::*pf2)() = &A::func2;
// A aa;
// (aa.*pf2)();
//
// return 0;
//}
// Date operator+(const Date& d, int x);
//int main()
//{
//
//
// return 0;
//}
//////////////////////////////////////////////////////////////////////
// 赋值运算符重载
//class Date
//{
//public:
// Date(int year = 1, int month = 1, int day = 1)
// {
// _year = year;
// _month = month;
// _day = day;
// }
//
// // Date d4(d3);
// Date(const Date& d)
// {
// _year = d._year;
// _month = d._month;
// _day = d._day;
// }
//
// // 11:40
// // d1 = d3 = d5
// // d1 = d1
// /*Date& operator=(const Date& d)
// {
// if (this != &d)
// {
// _year = d._year;
// _month = d._month;
// _day = d._day;
// }
//
// return *this;
// }*/
//
// void Print()
// {
// cout << _year << "-" << _month << "-" << _day << endl;
// }
//private:
// int _year;
// int _month;
// int _day;
//};
//
//int main()
//{
// Date d1(2025, 8, 1);
// Date d2(d1);
// // 一定注意,这个是拷贝构造
// Date d4 = d1;
//
// Date d3(2025, 10, 1);
// d1 = d3;
// Date d5(2025, 9, 1);
//
// d1 = d3 = d5;
//
// d1 = d1;
//
// return 0;
//}
///////////////////////////////////////////////////////
//#include"Date.h"
//
//int main()
//{
// /*Date d1(2025, 8, 1);
// Date d2 = d1 += 100;
// d1.Print();
// d2.Print();
//
// Date d3(2025, 8, 1);
// Date d4 = d3 + 100;
// d3.Print();
// d4.Print();*/
//
// Date d1(2025, 8, 1);
// Date ret1 = d1++;
// // Date ret1 = d1.operator++(10); // 显示调用,实参只要是整形就可以
//
// ret1.Print();
// d1.Print();
//
// Date d2(2025, 8, 1);
// Date ret2 = ++d2;
// //Date ret2 = d2.operator++();
// ret2.Print();
// d2.Print();
//
// return 0;
//}
void TestDate4()
{
const Date d1(2025, 9, 8);
d1.Print();
Date d3 = d1 + 100;
Date d2(2025, 9, 8);
d2.Print();
d2 += 10;
bool ret = d1 > d2;
Date* p1 = &d2;
const Date* p2 = &d1;
cout << p1 << " " << p2 << endl;
}
int main()
{
TestDate4();
return 0;
}