文章目录
日期类的实现
1、构造函数
//在类里声明,类外实现
Date::Date(int year, int month, int day)
{
if (year >= 1 &&
month <= 12 && month >= 1 &&
day >= 1 && day <= GetMonthDay(year, month))
{
_year = year;
_month = month;
_day = day;
}
else
{
cout << "非法日期" << endl;
}
}
2、拷贝构造
//代码小于5行,直接在类里面实现
Date(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
3、判断天数函数
int Date::GetMonthDay(int year, int month)
{
assert(year >= 0 && month > 0 && month < 13);//断言
const static int monthDayArray[13] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
//用static修饰数组,将数组放在静态区,不用每次执行判断函数都要开辟这块数组空间,提高效率
//再加一个const,让别人不能修改这个数组里的数据
if (month == 2 && isLeapYear(year))
{
return 29;
}
else
{
return monthDayArray[month];
}
}
4、运算符重载
4.1、加法
// d1 + 100 --> d1.operator+(day);
Date Date::operator+(int day)
{
Date ret(*this);//拷贝构造一个和d1一样的对象,这样就不会改变d1的值
ret._day += day;
while (ret._day > GetMonthDay(ret._year, ret._month))//天数超过该月天数就进1
{
ret._day -= GetMonthDay(ret._year, ret._month);//减去该月天数
ret._month++;
if (ret._month == 13)//如果月超过12,年就进1
{
++ret._year;
ret._month = 1;
}
}
return ret;//返回我们拷贝构造之后的对象
}
4.2、赋值运算符重载
Date& operator=(const Date& d)
{
if (this != &d)//判断是不是自己给自己赋值
{
_year = d._year;
_month = d._month;
_day = d._day;
}
return *this;
}
4.3、加等
// d1 += 100
Date& Date::operator+=(int day)
{
if (day < 0)//如果加的数是负数
return *this -= -day;//等于调用减等函数
_day += day;
while (_day > GetMonthDay(_year, _month))
{
_day -= GetMonthDay(_year, _month);
_month++;
if (_month == 13)
{
++_year;
_month = 1;
}
}
return *this;
//由于出了加等函数的作用域,*this还没有被销毁,所以我们可以用传引用返回
}
由于加法和加等的逻辑相同,所以我们可以复用加等的代码来实现加法
Date Date::operator+(int day)
{
Date ret(*this);
ret += day;
return ret;
}
4.4、减等、减法
// d1 -= 100
Date& Date::operator-=(int day)
{
if (day < 0)//如果减等数是负数
return *this += -day;//等于调用加等函数
_day -= day;
while (_day <= 0)
{
--_month;
if (_month == 0)
{
_month = 12;
--_year;
}
_day += GetMonthDay(_year, _month);
}
return *this;
}
由于减等和减法的逻辑相同,所以我们可以复用减等的代码来实现减法
Date Date::operator-(int day)
{
Date ret = *this;
ret -= day;
return ret;
}
4.5、前置++和后置++
- 由于前置++和后置++函数都是operator++()
- 所以C++规定,利用函数重载规则,不加参数的operator++()是前置++
- 加参数的operator++(int i)是后置++
//前置++代码
// ++d1
Date& operator++() // 前置
{
*this += 1;
return *this;//出了作用域*this还在,所以可以用传引用返回
}
后置++
//后置++代码
// d1++
Date operator++(int) //这里不具体接收参数的原因:传参只是为了让编译器判断是不是后置++
{
Date tmp(*this);
*this += 1;
return tmp;//出了作用域tmp就被销毁了,所以只能用传值返回,传值返回就需要拷贝
}
4.6、小于和小于等于
//小于运算符重载
bool Date::operator<(const Date& d)
{
if ((_year < d._year)
|| (_year == d._year && _month < d._month)
|| (_year == d._year && _month == d._month && d._day < d._day))
{
return true;
}
else
{
return false;
}
}
等于运算符重载
// d1 == d2
bool Date::operator==(const Date& d)
{
return _year == d._year
&& _month == d._month
&& _day == d._day;
}
- 实现了小于和等于函数之后,其他的比较大小的运算符都可以用小于函数和等于函数进行复用
- 由于代码复用,所以其他函数的代码行数都比较少,可以直接写在类里面,变成内联(inline)函数
// inline不支持声明和定义分别放到.h 和.cpp
// 所以成员函数中要成为inline最好直接在类里面定义
// 类里面定义默认就是inline
bool operator>(const Date& d)
{
return !(*this <= d);
}
bool operator>=(const Date& d)
{
return !(*this < d);
}
bool operator!=(const Date& d)
{
return !(*this == d);
}
// d1 <= d2
bool operator<=(const Date& d)
{
return *this < d || *this == d;
}
4.7、日期 减去 日期函数
// d1 - d2
int Date::operator-(const Date& d)
{
int flag = 1;
Date max = *this;
Date min = d;
if (*this < d)
{
min = *this;
max = d;
flag = -1;
}
int n = 0;
while (min != max)
{
++n;
++min;
}
return n * flag;
}
4.8、流插入<< 和 流提取>> 运算符重载
- 要想进行流提取和流插入的运算符重载,首先我们就要知道首先,cout是iostream中定义的ostream类的对象
- <<能用在cout上是因为,在ostream类里对<<进行了函数重载,把内置类型都重载了一遍
- cout << 1相当于执行函数cout.operator<<(1)
cout << "this"相当于执行函数cout.operator<<(“this”) - 如果在程序中希望对<<运算符进行重载,使其能够输出自定义的数据,如何实现呢?
由于ostream类型已经在iostream中实现,所以不能作为ostream类的成员函数重载,只能作为全局函数或友元函数重载。 - 流插入重载
//流插入重载
std::ostream& operator<<(std::ostream& out, const Date& d)
{
out << d._year << "_" << d._month << "_" << d.day << endl;
return out;
}
- 流提取重载
std::istream& operator>>(std::istream& in, Date& d)
//流提取不加const的原因:流插入就是把从流里面提取到的值写入到d里
//如果用了const就只能读不能写
{
in >> d._year >> d._month >> d.day;
return in;
}
- 注意
- 写这两个重载函数的时候要在类里面写友元函数来声明,否则访问不到私有的成员变量
- 友元函数表示让编译器知道这个函数是类的朋友,可以访问类的私有成员变量
friend std::ostream& operator<<(std::ostream out,const Date& d);//流插入
friend std::istream& operator>>(std::istream out,Date& d);//流提取
小技巧
1、如何判断该传值返回还是传引用返回
- 自定义类型的传值返回需要调用拷贝构造,所以为了提升代码效率,我们有时使用传引用返回
- 判断返回的对象除了函数作用域之后还存不存在,如果存在,就用传引用返回,如果不存在,就用传值返回
- 如果实在判断不出来就使用传值返回,传值返回一定是对的,当不一定是高效的
2、const修饰成员函数
- 建议成员函数中不修改成员变量的成员函数,都可以加上const
- 这样普通对象和const对象都可以调用
- 如果声明和定义分离,声明和定义都要加const
3、关于const修饰成员函数的几个小问题
- const对象可以调用非const成员函数吗?(不能,这是权限放大)
- 非const对象可以调用const成员函数吗?(可以,这是权限缩小)
- const成员函数内可以调用其他非const成员函数吗?(不能,这是权限放大)
- 非const成员函数内可以调用其他const成员函数吗?(可以,这是权限缩小)
//void Print(const Date* const this)
void Print() const//相当于上面那行代码
{
cout << _year << "-" << _month << "-" << _day << endl;
}