目录
下面通过实现日期类来复习使用类和对象的知识,其中每个接口的讲解和知识点的分析写在了代码中的注释中。
1.头文件Date.h
#pragma once
#include<iostream>
#include<assert.h>
using namespace std;
class Date
{
//友元声明,通过友元声明使全局函数能访问Date类的私有变量
friend ostream& operator<<(ostream& out, const Date& d);
friend istream& operator>>(istream& in, Date& d);
//void operator<<(ostream& out)
//{
// out << _year << "年" << _month << "月" << _day << "日" << endl;
//}
//写成成员函数,<<操作符的左操作数是this,右操作数是out
//调用时要写成下列形式:
//d1 << cout;
//d1.operator<<(cout);
//返回值为void不支持连续的调用
//所以写成全局函数较好
public:
// 全缺省的构造函数,这里缺省值写在声明中,定义中不用写
Date(int year = 1900, int month = 1, int day = 1);
//打印函数,因为该函数不会对对象进行修改,所以用const修饰
void Print() const;
//检查日期是否为有效日期的函数
bool CheckDate();
//获取对应天数的函数
//因为该函数短小且经常被调用,写在这默认为内联函数,可以减少函数栈帧的开销
int GetMonthDay(int year, int month)
{
assert(month < 13 && month > 0);
//定义数组用于获取天数
static int monthDayArray[] = { -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];
}
}
//由于日期类是没有涉及到资源的申请,所以拷贝构造和赋值运算符重载
// 都是浅拷贝,可以不需要自己实现,可以使用编译器自动生成的
//但是声明中写了,就需要实现,下面在Date.cpp文件中进行实现
// 拷贝构造函数
Date(const Date& d);
赋值运算符重载
Date& operator=(const Date& d);
//析构函数,没有资源申请不用实现
//~Date();
//逻辑运算符重载, 先实现一个小于和一个等于,其他的复用这两个进行实现
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;
// 日期+=天数
Date& operator+=(int day);
// 日期+天数
Date operator+(int day) const;
// 日期-=天数
Date& operator-=(int day);
// 日期-天数
Date operator-(int day) const;
//日期 - 日期 = 天数
int operator-(const Date& d) const;
// 前置++
Date& operator++();
// 后置++
Date operator++(int);
// 前置--
Date& operator--();
// 后置--
Date operator--(int);
private:
int _year;
int _month;
int _day;
};
//ostream 和 istream不支持拷贝构造,不能传值
ostream& operator<<(ostream& out, const Date& d);
istream& operator>>(istream& in, Date& d);
2.函数实现文件Date.cpp
#include "Date.h"
//不同文件中进行成员函数的声明和定义时,在定义的文件中函数名要加上类名
//检查日期是否为有效日期的函数
bool Date::CheckDate()
{
//天数不能大于对应月的最大天数且大于0
//月数不能大于12且不能小于0
if (_month < 1 || _month > 12
|| _day < 1 || _day > GetMonthDay(_year, _month))
{
return false;
}
else
{
return true;
}
}
// 全缺省的构造函数
Date::Date(int year, int month, int day) //定义和声明中只有一处给默认值,建议在声明中给
{
_year = year;
_month = month;
_day = day;
if (!CheckDate())
{
cout << "非法日期" << endl;
Print();
}
}
//打印函数
void Date::Print() const
{
cout << _year << "-" << _month << "-" << _day << endl;
}
// 拷贝构造函数 这里的拷贝构造和赋值运算符重载可以不用自己实现
Date::Date(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
赋值运算符重载
Date& Date::operator=(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
return *this;
}
//逻辑运算符重载, 先实现一个小于和一个等于,其他的复用这两个进行实现
bool Date::operator==(const Date& d) const
{
if (_year == d._year
&& _month == d._month
&& _day == d._day)
{
return true;
}
else
{
return false;
}
}
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)
{
if (_day < d._day)
{
return true;
}
else
{
return false;
}
}
else
{
return false;
}
}
else
{
return false;
}
}
bool Date::operator<=(const Date& d) const
{
if (*this < d || *this == d)
{
return true;
}
else
{
return false;
}
}
bool Date::operator>(const Date& d) const
{
if (!(*this <= d))
{
return true;
}
else
{
return false;
}
}
bool Date::operator>=(const Date& d) const
{
if (!(*this < d))
{
return true;
}
else
{
return false;
}
}
bool Date::operator!=(const Date& d) const
{
if (!(*this == d))
{
return true;
}
else
{
return false;
}
}
// 日期+=天数
Date& Date::operator+=(int day)
{
if (day < 0)
{
*this -= (-day);
return *this;
}
//先加天数,超过当前月的天数进位,再加月数,超过12之后向前进位
day = _day + day;
while (day > GetMonthDay(_year, _month))
{
day -= GetMonthDay(_year, _month); //天数先减,因为这个天数取的是当前月的天数,不是下一个月的天数
_month++;
if (_month == 13)
{
_year++;
_month = 1;
}
}
_day = day;
return *this;
}
// 日期+天数
Date Date::operator+(int day) const
{
Date tmp = *this;
tmp += day;
return tmp;
}
// 日期-=天数
Date& Date::operator-=(int day)
{
if (day < 0)
{
*this += (-day);
return *this;
}
while (_day <= day)
{
_month--;
if (_month == 0)
{
_year--;
_month = 12;
}
_day += GetMonthDay(_year, _month);
}
_day = _day - day;
return *this;
}
// 日期-天数
Date Date::operator-(int day) const
{
Date tmp = *this;
tmp -= day;
return tmp;
}
//日期 - 日期 = 天数
int Date::operator-(const Date& d) const
{
Date max = *this;
Date min = d;
int flag = 1;
if (*this < d)
{
max = d;
min = *this;
flag = -1;
}
int count = 0;
while (min != max)
{
min += 1;
count++;
}
return count * flag;
}
// 前置++
Date& Date::operator++()
{
*this += 1;
return *this;
}
// 后置++
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;
}
//ostream 和 istream不支持拷贝构造,不能传值
ostream& operator<<(ostream& out, const Date& d)
{
cout << d._year << "年" << d._month << "月" << d._day << "日" << endl;
return cout;
}
istream& operator>>(istream& in, Date& d)
{
while (1)
{
cout << "请一次输入年月日:>";
in >> d._year >> d._month >> d._day;
if (!(d.CheckDate()))
{
cout << "输入日期非法";
d.Print();
cout << "请重新输入!" << endl;
}
else
{
break;
}
}
return in;
}
2.1说明‘-’复用'-='和‘-='复用‘-’的区别
这段代码是自己实现'-='功能,'-'通过复用'-='来实现。
// 日期-=天数
Date& Date::operator-=(int day)
{
if (day < 0)
{
return *this += -day;
}
_day -= day;
while (_day <= 0) //为0也要借
{
--_month;
if (_month == 0)
{
_month = 12;
--_year;
}
_day += GetMonthDay(_year, _month); //借上个月的天数
}
return *this;
}
// 日期-天数
Date Date::operator-(int day) const
{
Date tmp = *this;
tmp -= day;
return tmp;
}
这段代码是自己实现'-'功能,'-='通过复用'-'来实现。
// 日期-=天数
Date& Date::operator-=(int day)
{
*this = *this - day;
return *this;
}
// 日期-天数
Date Date::operator-(int day)
{
Date tmp = *this;
tmp._day -= day;
if (day < 0)
{
return tmp += -day;
}
tmp._day -= day;
while (_day <= 0) //为0也要借
{
--tmp._month;
if (tmp._month == 0)
{
tmp._month = 12;
--tmp._year;
}
tmp._day += GetMonthDay(tmp._year, tmp._month); //借上个月的天数
}
return tmp;
}
情况1:
-= 自己实现:全程没有拷贝
- 复用 -= : 在创建tmp时和返回临时变量tmp时有两次拷贝
情况2:
- 自己实现: 全程只有在创建tmp时和返回临时变量tmp时有两次拷贝
-= 复用 - :复用 - 时有两次拷贝,还有一次赋值则有一次拷贝,故有3次拷贝
上述两种情况对比:
对于 - 来说,情况1和情况2都有两次拷贝
对于 -= 来说,情况2比情况1多了3次拷贝
区别:上述的两个情况只有在使用'-='这个接口时有区别,就是自己实现'-='会少3次拷贝,效率更高。所以以后我们先实现'-=',然后'-'的实现复用'-='进行实现。
2.2前置++和后置++的说明
这里解释一下前置++和后置++的区别,前置--和后置++类似,这里不做解释。
// 前置++
Date& Date::operator++()
{
//先++在用
*this += 1;
//返回值返回后给表达式用的,前置++和后置++都要改变*this的值
//前置++,先++后返回++后的值给表达式用
return *this;
}
// 后置++
Date Date::operator++(int)
{
Date tmp = *this;
*this += 1;
//后置++,++之后返回++之前的值给表达式用
return tmp;
}
3.测试代码Test.cpp
#include "Date.h"
void test1()
{
//测试默认构造函数
Date d1;
d1.Print();
Date d2(2024, 8, 26);
d2.Print();
Date d3(2024, 8, 34);
cout << d3.CheckDate() << endl;
测试获得每个月的对应天数
//cout << d3.GetMonthDay(2019, 2) << endl;
}
void test2()
{
//测试拷贝构造和赋值运算符
Date d1(2024, 8, 26);
d1.Print();
Date d2 = d1;
d2.Print();
Date d3;
d3 = d1;
d3.Print();
}
void test3()
{
//测试逻辑运算符
Date d1(2024, 8, 31);
Date d2(2024, 8, 26);
Date d3 = d2;
cout << (d2 == d1) << endl;
cout << (d2 < d1) << endl;
cout << (d2 <= d1) << endl;
cout << (d2 > d1) << endl;
cout << (d2 >= d1) << endl;
cout << (d2 != d1) << endl;
cout << (d2 == d3) << endl;
cout << (d2 < d3) << endl;
cout << (d2 <= d3) << endl;
cout << (d2 > d3) << endl;
cout << (d2 >= d3) << endl;
cout << (d2 != d3) << endl;
}
void test4()
{
//测试日期的加减
//测试+=
Date d1(2024, 8, 24);
d1 += -2000;
d1.Print();
//测试+
Date d2(2024, 8, 26);
d2 = d2 + 1000;
d2.Print();
//测试-=
Date d3(2024, 8, 26);
d3 -= (-2000);
d3.Print();
//测试-
Date d4(2024, 8, 31);
d4 = d4 - 100;
d4.Print();
//测试日期 - 日期
Date d5(2024, 8, 26);
Date d6(2027, 1, 1);
cout << (d5 - d6) << endl;
}
void test5()
{
//测试++和--
Date d1;
Date d2(d1);
Date ret1 = ++d1;
d1.Print();
ret1.Print();
Date ret2 = d2++;
d2.Print();
ret2.Print();
Date d3;
Date d4(d3);
Date ret3 = --d3;
d3.Print();
ret3.Print();
Date ret4 = d4--;
d4.Print();
ret4.Print();
}
void test6()
{
//测试输入输出
Date d1;
Date d2;
cin >> d1 >> d2;
cout << d1 << d2 << endl;
}
int main()
{
//test1();
//test2();
//test3();
//test4();
//test5();
//test6();
return 0;
}