类与对象 -- 日期类实现
我们要结合之前学习的类的默认成员函数来实现一个简单的日期类
#pragma once
#include<iostream>
using namespace std;
#include<assert.h>class Date
{//流插入和流提取重载声明为友元,可以直接访问私有成员变量friend ostream& operator<<(ostream& out, const Date& d);friend istream& operator>>(istream& in, Date& d);private:int _year;int _month;int _day;public://默认构造函数Date(int year = 1900,int mouth = 1,int day = 1);//void print() const; //这种不需要值变的直接声明为const成员函数//写一个函数查询每个月的天数,声明在类内默认内联,因为要频繁调用int GetMouthDay(int year,int mouth){//1.首先确保月份数合法assert(mouth > 0 && mouth <13);//2.使用静态数组保存每个月的天数//使用静态是因为只用初始化一次,要是不使用static,则每次调用都需要创建初始化这个数组,因为函数栈帧结束就会销毁,需要重新构建//适用场景:这种直接调用的数据,static保证了生命周期也保证了内存的节省static int mouthDayArray[13] = {-1,31,28,31,30,31,30,31,31,30,31,30,31};//首个元素设置为-1,这样契合从一月开始的语言逻辑//3.闰年判断,来改变闰年2月的天数if(mouth == 2 && ((year % 4 == 0 && year % 100 != 0 ) || (year % 400 == 0))){return 29;}else {return mouthDayArray[mouth];}}bool checkDate(); //检查日期合法性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);Date operator+(int day) const;// d1 -= 天数Date& operator-=(int day);Date operator-(int day) const;// d1 - d2int operator-(const Date& d) const;// ++d1 -> d1.operator++()Date& operator++();// d1++ -> d1.operator++(0)// 为了区分,构成重载,给后置++,强⾏增加了⼀个int形参// 这⾥不需要写形参名,因为接收值是多少不重要,也不需要⽤// 这个参数仅仅是为了跟前置++构成重载区分Date operator++(int);Date& operator--();Date operator--(int); //与++实现思路一致};// 重载
ostream& operator<<(ostream& out, const Date& d);
istream& operator>>(istream& in, Date& d);
#include"Date.h"//检查日期是否合法
bool Date::checkDate() //普通成员函数隐式传this作为首个参数
{if(_month < 1 || _month > 12 || _day < 1 || _day > GetMouthDay(_year,_month)){return false;}else{return true;}
}//重写构造函数
Date::Date(int year,int mouth,int day)
{_year = year;_month = mouth;_day = day;if(!checkDate()){cout << "日期非法" << endl;}
}void Date::print()const
{cout << _year << "-" << _month << "-" << _day << endl;
}//d1 < d2
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;}}return false;
}bool Date::operator==(const Date& d) const
{return _year == d._year&& _month == d._month&& _day == d._day;
}//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 !(*this == d);
}//给日期加上一个天数,返回加后的日期本身
Date& Date::operator+=(int day)
{if(_day < 0){return *this -= -_day;}_day += day;while(_day > GetMouthDay(_year,_month)) //while可以多次判断直到满足位置{_day -= GetMouthDay(_year,_month);_month++;if(_month == 13){++_year;_month = 1;}}return *this;
}Date Date::operator+(int day)const
{Date tmp = *this;tmp += day;return tmp;
}//这里-=和- 与上两个类似就不实现了//重载后置++
Date Date::operator++(int) //这里的传参是与前置++区分,无实际意义
{Date tmp(*this);*this += 1;return tmp;
}//重载前置++
Date& Date::operator++()
{*this += 1;return *this;
}//前置后置--与++实现逻辑一致//重载对象相减d1 - d2
int Date::operator-(const Date& d)const
{//有两种可能,d1大或者d2大//默认一下初始大和小Date max = *this;Date min = d; int flag = 1; if(max < min){max = d;min = *this;flag = -1;}int n = 0;while(min != max){++min;++n;}return n * flag;
}//重载流插入运算符
//需要契合操作习惯 cout << " " << endl;
ostream& operator<<(ostream& out,const Date& d)
//返回引用的目的是直接改变原值而不是改变拷贝,而且本身就无法被拷贝
//返回类型是标准输出流类型,传的参数是cout的别名,因为需要链式访问,所以传引用返回
//第二个参数是类对象引用
//声明为友元是要访问私有成员变量
{out << d._year << "年" << d._month << "月" << d._day << "日" <<endl;return out;
}istream& operator>>(istream& in ,Date& d)
{cout << "请依次输入年月日>:" ;in >> d._year >> d._month >> d._day;//输入当然还要检查是否合法if(!d.checkDate()){cout << "日期非法 " <<endl;}return in;
}
主要注意的是流插入和流提取的重载,里面包含了传引用返回的思想,已经传引用返回实现链式访问,就可以连续调用重载的运算符了。
总结:
bool operator<(const Date& d) const; // 不修改对象状态 这种不需要修改成员变量的函数,给后面加const保证安全性,且允许const对象调用
// 复合赋值运算符:返回引用 Date& operator+=(int day);// 算术运算符:返回新对象 Date operator+(int day) const;// 比较运算符:返回bool bool operator<(const Date& d) const;
// 前置++:返回引用 Date& operator++() {*this += 1;return *this; }// 后置++:返回临时对象,int参数用于区分 Date operator++(int) {Date tmp(*this);*this += 1;return tmp; }
- 友元函数的合理使用
friend ostream& operator<<(ostream& out, const Date& d); friend istream& operator>>(istream& in, Date& d);流操作符需要访问私有成员,必须声明为友元友元打破了封装,但要谨慎使用
- 重载cout cin的链式调用
ostream& operator<<(ostream& out, const Date& d) {out << d._year << "年" << d._month << "月" << d._day << "日";return out; // 返回引用支持链式调用 }返回流引用(ostream&)支持连续输出
- 对象声明周期的管理
Date Date::operator+(int day) const {Date tmp = *this; // 创建临时对象tmp += day;return tmp; // 返回临时对象 }//传引用返回会出问题哦