天天看点

日期类的实现(C++)

日期类的实现

前面介绍了类的一些默认成员函数, 这里进行一个小练习, 试着实现一下日期类:

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]; //通过月份拿到天数
		//二月特殊处理 以及闰年(如果是闰年的2月 则多一天)
		if (month == 2
			&& ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)))
			++day;
		return day;
	}
	// 打印函数 用d.print()调用
	void print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
	// 全缺省的构造函数 用Date d(num,num,num)调用
	Date(int year = 1, int month = 1, int day = 1){
		//判断参数是否有效, 如果没有效 则给一个默认值
		// 由于天数不好获取, 则给一个函数getMonthday()
		if (year <= 0
			|| month <= 0 || month > 12
			|| day > getMonthDay(year, month)){
			cout << "非法日期!" << endl;
			_year = _month = _day = 1;
			cout << "日期重置为:1-1-1" << endl;
		}
		else{
			_year = year;
			_month = month;
			_day = day;
		}
	}
	// 拷贝构造
	Date(const Date& date){
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}
	// 赋值运算符重载
	Date& operator=(const Date& date){
		if(this != &d){
			_year = d._year;
			_month = d._month;
			_day = d._day;
		}
	return *this;
	}
	// 析构  
	~Date(){};
	
           
  • 对于+=运算, 相当于 d.operator+=(day), 简化为d += day, 而d会变, day是给定的不会变, 返回的是修改之后的d的值, d本身会被改掉, 而对于+=成员函数来说, d就是this指针所指向的对象,
  • 所以修改的是this指针所指向对象的成员, 返回的是被修改的对象本身 则return *this,
  • *this就指的是对象, 而返回值 Date& 相当于是对象的别名 (返回对象用引用接收)
在这里插入代码片	
	// 日期+=天数 用d += day调用 返回+=之后左操作数的内容 返回引用效率高
	Date& operator+=(int day){ // 这里返回值也可以 只不过又多了一次值拷贝
		//判断是否为负值 当天数是负的时 进行复用 调-=操作 加一个负的天数=减一个正的天数
		if (day < 0)
			return *this -= -day;
		// 给日期+=天数, 给日期的_day+=传入的day
		_day += day;
		//判断+=之后的天数是否超出当前月份的天数
		while (_day > getMonthDay(_year, _month)){
			//进位: 如果超出, 当前天数减去当前月的天数,然后月份进位
			_day -= getMonthDay(_year, _month);
			++_month;

			//判断年份是否需要进位 如果是12月份进位上来的就是13月份 再进年
			if (_month == 13){
				++_year;
				_month = 1; // 将月份置为1
			}
		}
		return *this;
	}
	// 日期+天数 date + day调用, 单纯加法, 操作数本身是不会发生任何变化的,
	//  比如 a+b=c 结果并不会影响 a,b 本身的值, 所以应该返回拷贝, 
	Date operator+(int day){
		// date和day的内容都不会变, 即*this和day的值不能变, 只返回相加后的结果
		Date ret(*this); // 当前*this的值是不能改的
		return ret += day;// 给副本进行+=操作
	}
	
	// 日期-=天数 用d -= day调用
	Date& operator-=(int day){
		// 同样若减去一个负数, 则-=一个负数等于+=一个整数 调+=操作
		if (day < 0)
			return *this += -day;
		// 先用当前天数 -= 传入的天数 再判断-=后的天数_day
		_day -= day;

		//判断是否需要退位, 若此时_day <= 0, 则先退位 再去加上退位之后的月的天数
		while (_day <= 0){  // 等于0也不行, 因为每个月没有0号这一天
			//退位
			--_month;
			//判断是否需要进行年份的退位
			if (_month == 0){
				--_year;
				_month = 12;
			}
			// 由于此时的_day <= 0, 则+=当前月的天数 实际上是当前月的天数减去/_day/
			_day += getMonthDay(_year, _month);
		}
		return *this;
	}
	// 日期-天数  date - day调用 单纯减法, 操作数本身也是不会发生任何变化的, 返回拷贝
	Date operator-(int day){
		// date和day的内容都不会变, 即*this和day的值不能变, 只返回相减后的结果
		Date ret(*this);
		return ret -= day;
	}
           
  • 这里实现自加和自减, 前置和后置的区别在于前者返回的是对象本身, 后者的返回的不是对象本身, 所以不用引用接收, 而返回的则是一个匿名对象,
  • 后置操作中, 因为要返回操作之前的值, 所以在实现自加或者自减之前为了保存副本需通过拷贝构造创建一局部对象,最终在返回时再次通过这个局部对象使用拷贝构造创建一匿名对象供后面使用
  • 为了区分前置和后置, 将前置函数写为返回类型 operator+运算符(), 而后置操作函数则为返回类型 operator+运算符(内置类型), 两个函数都有this指针, 为了区分在其不同, 在其参数列表加入一内置类型, 对象在调用这两函数时进行操作时, 主要还是看除了this指针还有没有第二个参数 而第二个参数并没有实际的含义 并不用传入任何参数, 只是为了编译器区分, 编译器默认()中有参为后置, 无参为前置
// 前置++  ++date --> 首先自加1,再返回自加之后的值 --> d.operator++()
	Date& operator++(){ // 底层是Date& operator++(Date* this), 
		// *this+=1后相当于d对象已经发生变化了, 然后再返回this指针指向的当前对象
		// +=操作就会更新当前操作后的变化
		return *this += 1; 
	}
	
	// 后置++ date++ --> 返回自加之前的值
	// 由于返回的是自加之前的值, 而date本身也进行了自加的, 所以不能返回date本身
	// 所以后置返回拷贝 前置返回本身 而要返回自加之前的值, 可以先把自加之前的值保存
	Date operator++(int){
		Date ret(*this) // 给一拷贝构造, 先把没有++之前的对象保存下来
		*this += 1; // 相当于(*this).operator += (1), 
		return ret; // 最终返回++之前的对象 在返回时再做一次拷贝构造返回值
	}
	// 前置-- --date调用  首先自减1,再返回自减之后的值
	Date& operator--(){
		return *this -= 1;
	}

	// 后置--  date--调用  返回自减之前的值
	Date operator--(int){
		Date ret(*this);
		*this -= 1;
		return ret;
	}
           

为了清楚上述前置函数和后置函数区别, 下面这里将其写作非成员函数供参考.

// 前置++
Date& operator++(const Date& date);
// 后置++
Date operator++(const Date& date, int); //  这里的int只做标记 区分前置和后置

           

下面是日期的比较: 先比较年 再到月 再到日(bool: 为真时返回1,假时返回0)

  • 在函数的形参中传引用 说明该参数是在函数外边的, 若是函数的局部变量, 则不需要传引用, 哈数调用完自动释放, 且不用引用接收返回值, 引用的声明周期大于函数的生命周期
// 日期比较
	// <运算符重载 
	bool operator<(const Date& date){
		if (_year < date._year)
			return true;
		else if (_year == date._year){
			if (_month < date._month)
				return true;
			else if (_month == date._month){
				if (_day < date._day)
					return true;
			}
		}
		return false;
	}
	//  >运算符重载  大于是小于等于的取反
	bool operator>(const Date& date){
		return !(*this <= date);
	}
	// ==运算符重载 
	// 判等 年-月-天都进行判断 当前的对象的年月天 是否等于 传入date的年月天
	// cout << (d1 == d2) << endl 调用并输出结果
	bool operator==(const Date& date){
		return _year == date._year
			&& _month == date._month
			&& _day == date._day;
	}
	// >=运算符重载 大于等于就是大于或等于
	bool operator>=(const Date& date){
		return *this > date || *this == date;
	}
	// <=运算符重载  小于等于就是小于或等于
	bool operator<=(const Date& date){
		return *this < date || *this == date;
	}
	// !=运算符重载  不等于就是等于的取反
	bool operator!=(const Date& date){
		return !(*this == date);
	}
	// 日期-日期 返回天数
	// eg: 2020.5.13  -  2019.4.15  就是算在后者不变的前提上加上多少天能到前者 
	// 看date何时能和*this相等 前者对象调用该函数 *this表前者对象
	int operator-(Date& date){
		if (*this > date){
			int day = 0;
			Date ret = date;// date本身不能变
			while (ret != *this){
				++ret;
				++day;
			}
			return day;
		}
		else{
			Date ret = date;
			int day = 0;
			while (ret != *this){
				--ret;
				--day;
			}
			return day;
		}
	}
private:
	int _year;
	int _month;
	int _day;
};
           

继续阅读