这一讲主要实现以下类和对象的一个实例------日期类,会用到之前讲过的类的知识。
在介绍这个之前,我需要先补充一点关于前置++后置++前置--后置--的运算符重载、以及cincout的运算符重载。
首先是++--,当运算符重载的时候,假如你写成成员变量,那么默认this指针指向的对象就是第一个操作数,对于++--这种一元运算符来说,我们是不用再传其他参数的。
但是由于C++规定的默认参数顺序问题,这里没办法对前置++后置++区分,甚至你连重载都做不到,因为不论前置后置,++都是一样的,你的函数名只能是operator++,参数只能是this,只有返回值时可能不同的,但是返回值相同与否和重载没什么关系,为了解决上述问题,C++规定,后置++需要传一个int形参,以作区分。--也是类似。
下面是cin,cout其实也就是<< >>流插入流提取运算符的重载,二元运算符,对于<<来说,ostream其实也就是cout是第一个操作数,我们要打印的对象,也就是this指针(其实这么说不准确)就是第二个操作数,你发现异常了没有?当我们重载操作符到成员函数时,this必须是第一个操作数,假如我们写成成员函数,那就变成了*this << out,这也太奇怪了,所以我们把这两个写成全局函数,就可以解决这个问题。
值得注意的是,当我们调用<<运算符时,需要支持链式调用,即cout << a <<b;意味着打印完a之后,接着还需要ostream&对象也就是cout接着作为<<第一个操作数打印b,所以我们需要返回值,>>同理。
还有一点需要注意,当我们写成全局函数的时候,是不能调用默认为private的成员变量的,那时候就要在类中将这两个重载运算符函数置为友好函数,也就是函数声明前加上friend。
接下来展示一下Date代码
Date.h
cpp
#pragma once
#include <iostream>
using namespace std;
class Date
{
friend ostream& operator<<(ostream& out, const Date& d);
friend istream& operator>>(istream& in, const Date& d);
public:
bool CheckDate() const;
// 获取某年某月的天数
int GetMonthDay(int year, int month) const;
// 全缺省的构造函数
Date(int year = 1900, int month = 1, int day = 1);
// 拷贝构造函数
// // d2(d1)
//Date(const Date& d);
// 赋值运算符重载
// d2 = d3 -> d2.operator=(&d2, d3)
Date& operator=(const Date& d);
//// 析构函数
//~Date();
//日期+=天数
Date& operator+=(int day);
// 日期+天数
Date operator+(int day);
// 日期-天数
Date operator-(int day);
// 日期-=天数
Date& operator-=(int day);
// 前置++
Date& operator++();
// 后置++
Date operator++(int);
// 后置--
Date operator--(int);
// 前置--
Date& operator--();
// >运算符重载
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);
// 日期-日期 返回天数
int operator-(const Date& d);
void print()
{
cout << _year << '/' << _month << '/' << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
Date.cpp
cpp
#define _CRT_SECURE_NO_WARNINGS 1
#include "Date.h"
// 获取某年某月的天数
int Date::GetMonthDay(int year, int month) const
{
static int m_day[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 m_day[month] + 1;
}
return m_day[month];
}
// 全缺省的构造函数
Date::Date(int year , int month , int day )
{
_year = year;
_month = month;
_day = day;
}
//// 拷贝构造函数
// // d2(d1)
//Date::Date(const Date& d)
//{
// _year = d._year;
// _month = d._month;
// _day = d._day;
//}
// 赋值运算符重载
// d2 = d3 -> d2.operator=(&d2, d3)
Date& Date::operator=(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
return *this;
}
// 日期+=天数
Date& Date::operator+=(int day)
{
_day += day;
while (_day > GetMonthDay(_year, _month))
{
_day -= GetMonthDay(_year, _month);
_month++;
if (_month == 13)
{
_month = 1;
_year++;
}
}
return *this;
}
// 日期+天数
Date Date::operator+(int day)
{
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._month = 1;
tmp._year++;
}
}
return tmp;*/
tmp += day;
return tmp;
}
// 日期-天数
Date Date::operator-(int day)
{
Date tmp = *this;
tmp._day -= day;
while (tmp._day <= 0)
{
if (tmp._month == 1)
{
tmp._day += GetMonthDay(tmp._year, 12);
}
else
tmp._day += GetMonthDay(tmp._year, tmp._month-1);
tmp._month--;
if (tmp._month == 0)
{
tmp._month = 12;
tmp._year--;
}
}
return tmp;
}
// 日期-=天数
Date& Date::operator-=(int day)
{
*this = *this - day;
return *this;
}
// 前置++
Date& Date::operator++()
{
_day++;
if (_day > GetMonthDay(_year, _month))
{
_day = 1;
_month++;
if (_month == 13)
{
_month = 1;
_year++;
}
}
return *this;
}
// 后置++
Date Date::operator++(int)
{
Date tmp = *this;
++(*this);
return tmp;
}
// 后置--
Date Date::operator--(int)
{
Date tmp = *this;
(this->_day)--;
if (_day == 0)
{
if (_month == 1)
{
_day = GetMonthDay(_year, 12);
_month = 12;
_year--;
}
else
_day = GetMonthDay(_year, _month - 1);
}
return tmp;
}
// 前置--
Date& Date::operator--()
{
(*this)--;
return *this;
}
// >运算符重载
bool Date::operator>(const Date& d)
{
if (_year == d._year)
{
if (_month == d._month)
{
return _day > d._day;
}
else
return _month > d._month;
}
return _year > d._year;
}
// ==运算符重载
bool Date::operator==(const Date& d)
{
return _year == d._year
&& _month == d._month
&& _day == d._day;
}
// >=运算符重载
bool Date::operator >= (const Date& d)
{
return (*this) > d || (*this) == d;
}
// <运算符重载
bool Date::operator < (const Date& d)
{
return !((*this) >= d);
}
// <=运算符重载
bool Date::operator <= (const Date& d)
{
return !((*this) > d);
}
// !=运算符重载
bool Date::operator != (const Date& d)
{
return !((*this) == d);
}
// 日期-日期 返回天数
int Date::operator-(const Date& d)
{
//0 this 1 d
int min = 0;
if (*this > d)
{
min = 1;
}
int day = 0;
Date lit;
Date big;
int flag = -1;
if (min == 0)
{
lit = *this;
big = d;
}
else
{
lit = d;
big = *this;
flag = 1;
}
while (lit != big)
{
++lit;
day++;
}
return flag * day;
}
ostream& operator<<(ostream& out, const Date& d)
{
cout << d._year << "年" << d._month << "月" << d._day << "日" << endl;
return out;
}
istream& operator>>(istream& in, const Date& d)
{
while (1)
{
cout << "请依次输入年月日->";
in >> d._year >> d._month >> d._day;
if (!d.CheckDate())
{
cout << "日期非法" << endl;
}
else
{
break;
}
}
return in;
}
bool Date::CheckDate() const
{
if (_month > 12 || _month < 1)
return false;
else if (_day != GetMonthDay(_year, _month))
return false;
else
return true;
}
这个程序实现了日期的一些基本操作,比如加减天数,减去日期,自增自减,等等。
在最后,我来介绍一下最后一个需要跟大家讲的默认成员函数,即取地址运算符重载函数。
先来介绍以下const成员函数,我们上边其实已经用到了const成员函数,定义const成员函数的方式很简单,在函数声明、定义后加上const即可,默认我们的this指针是Date * const this,之前我们讲指针的时候也聊过,在*后加const,意味着不能修改this的指向,但不代表不能修改*this,那么这是否会造成安全隐患呢?比如我们简单写个+-运算符的重载函数,你却可以修改*this,这不太安全也不符合逻辑,而将成员函数修改为const成员函数后,参数就变成了const Date* const this,可以解决这个问题。
值得注意的是,const对象是不能调用普通成员函数的,这被视为是权限的放大,但是普通对像是可以调用const成员函数,权限缩小无所谓。
取地址运算符重载函数。
这个函数一般来讲是不用手动实现的,其实说是一个,其实是一种,取地址运算符重载成员函数也有const和非const两种,前者返回const对象,本身是const成员函数,后者则是普通,当程序中都有时,优先调用const,这其实没啥可讲的,因为你就是返回一个this就好了,this本身就是地址,除非你想瞎返回一个地址才需要手动实现。