作者:chlorine
专栏:c++专栏
我的花一定会开。
【学习目标】
- 拷贝复制——赋值运算符重载
目录
🎓运算符重载(-><=...)
🎓日期&天数
🎓前置++和后置++重载
我们完成了赋值运算符重载章节的学习,对operator关键字的使用有了一定的了解,接下来我们要来实现相对完整的日期类。
#include<iostream>
using namespace std;
class Date
{
public:
Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
Date(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
bool operator<(const Date& d)
{
if (_year < d._year)
{
return true;
}
else if (_year == d._year && _month < d._month)
{
return true;
}
else if (_year == d._year && _month == d._month && _day < d._day)
{
return true;
}
else
{
return false;
}
}
void print()
{
cout << _year << "-" << _month << "-" << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1(2023, 10, 5);
Date d2(2023, 11, 5);
int ret=d1<d2;
cout << ret << endl;
Date d3(d1);
d1.print();
d2.print();
d3.print();
return 0;
}
我们要对以上的代码进行声明定义分离。
拷贝构造需要写嘛?赋值重载需要写嘛?析构需要写嘛?——都不需要
唯一要写的就是——构造!
让我们回顾一下类的俩种定义方式:
1. 声明和定义全部放在类体中,需注意:成员函数如果 在类中定义 ,编译器可能会将其当成 内联函数 处理。 类声明放在 .h 文件中,成员函数定义放在 .cpp 文件中,注意: 成员函数名前需要加类名 ::声明和定义分离,不能同时(声明和定义)都给构造函数缺省参数。
注意:成员函数名前需要加类名::
所以我们对于构造函数的声明定义,应以下形式:
好嘞,接下来真正的来实现了。
🎓运算符重载(-><=...)
对于我们d1的日期大于另一个日期,就返回true,但是如果小于呢,如果小于等于,等于,大于等于呢.....我们每次都要改下面一段代码嘛?未必也太麻烦了吧
bool Date::operator<(const Date& d)
{
if (_year < d._year)
{
return true;
}
else if (_year == d._year && _month < d._month)
{
return true;
}
else if (_year == d._year && _month == d._month && _day < d._day)
{
return true;
}
else
{
return false;
}
}
bool Date::operator==(const Date& d)
{
return _year == d._year
&& _month ==d._month
&& _day == d._day;
}
我们首先写了 (< 和 ==)运算符的运算符重载函数,接下来我们要对 (> >= <= )进行操作。因为==和<可以完成后续的操作符的函数写照。
- 首先我们看看<=
我们先可以形成 d1<=d2
//d1<=d2 bool Date::operator<=(const Date& d)
d1是this指针,d2是d,所以我们就可以想到,<=不就是 <和=的结合嘛?
//d1<=d2 bool Date::operator<=(const Date& d) { return *this < d || *this == d; }
那么接下来的 >= 和 > 不就由任而解了嘛~
那我就将完整代码给你们看看。
//.h文件中声明 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);
//.cpp文件中定义 bool Date::operator<(const Date& d) { if (_year < d._year) { return true; } else if (_year == d._year && _month < d._month) { return true; } else if (_year == d._year && _month == d._month && _day < d._day) { return true; } else { return false; } } 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); }
完美的进行了一波复用。这个代码不仅支持日期类,也支持任意类型的复用。
重载一个<和=,或者重载一个>和=,都是可以用复用,因为它们本身都存在互斥关系。
其实这里我们讲述一个通用的方法,以后会有更多类型使用复用。
🎓日期&天数
日期类还有另一种方式的。
日期+日期 显然没有意义的,有日期-日期有意义,日期+-天数也是有意义的。接下来我们实现这段函数重载。
如果我想算一下从今天2023/11/14,那么一百天是哪一天?
我们首先要知道,每一个月份的天数是不一样的,特别是2月得考虑平年闰年,闰年29天,平年28天,闰年的判断规则是(四年一润,百年不润,再400年润)挺复杂的,获取每个月的天数,首先我们得完成。
int Date::GetMonthDay(int year, int month) { int daysArr[13] = { 0,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 daysArr[month]; } }
那我们接下来就写一下下面函数
//d+100
Date& operator+(int day);
Date& Date::operator+(int day)
{
_day += day;
while (_day > GetMonthDay(_year, _month))
{
_day -= GetMonthDay(_year, _month);
++_month;
if (_month == 13)
{
++_year;
_month = 1;
}
}
return *this;
}
直到_day小于当月的天数,就给月份+1,如果月份=13,就给年份+1.
结果是2024/2/22
但是这样写的话,是不是给d1本身改变了?如何不让其本身改变呢?就像i=10,i+100中的i会不会变?——不会变,因为会有返回值,但是这里的d1改变了。其实这里我们对d1+100,严格上完成的是+=,+=有返回值。所以不改变原来的值。
+不能改变自己
- 如何用+来进行保存d1的原本的值呢?——临时变量tmp
- 如何利用临时变量呢?——拷贝构造
大家有没有发现这里是个大杂烩,前面的知识进行融会贯通,这里就考察了拷贝构造,d1+100,d1是this,那么就将this拷贝给tmp,然后将tmp+day,返回tmp临时变量,不用引用返回,出了函数就销毁了。
//d1+day
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._year;
tmp._month = 1;
}
}
return tmp;
}
这就实现了用+来保留d1的原有值,不改变它的值,创建临时变量进行拷贝构造,就可以用+来实现。这里我还要增加一个点,这里+=和+我们有没有联想到上面的复合运用?如果用+=复合+,又如何用+复合+=?
//+=复用+
Date& Date::operator+=(int day)
{
*this = *this + day;
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._year;
tmp._month = 1;
}
}
return tmp;
}
//+复用+=
Date Date::operator+(int day)
{
Date tmp(*this);
tmp += day;
return tmp;
}
Date& Date::operator+=(int day)
{
_day += day;
while (_day > GetMonthDay(_year, _month))
{
_day -= GetMonthDay(_year, _month);
++_month;
if (_month == 13)
{
++_year;
_month = 1;
}
}
return *this;
}
按上面的代码,更推荐的是第二种。
🎓前置++和后置++重载
- 前置++:返回++之后的对象(默认前置++,参数增加int)
- 后置++:返回++之前的对象
这样就可以吗?运算符重载和函数重载都用了重载的词,但是不一样的,运算符重载是自定义类型,函数重载是函数名相同参数不同。这里是无法进行函数重载的,因为函数名相同参数也相同。为了让它们重载呢?我们就在后置++增加一个int参数,int参数并不是接收具体的值,仅仅是占位。
//前置++
Date& Date::operator++()
{
*this+= 1;
return *this;
}
//后置++
Date Date::operator++(int)
{
Date tmp=*this;
*this += 1;
return tmp;
}
以示区分。前置++,后置++。内置类型的前置后置++效率没有什么问题,自定义类型的前置后置++有区别,看上面前置和后置的代码,而且编译器自动默认前置,而且后置创建了临时变量。
前置++和后置++都是一元运算符,为了让前置++与后置++形成能正确重载
- 前置++:返回+1之后的结果 注意:this指向的对象函数结束后不会销毁,故以引用方式返回提高效率。
- C++规定:后置++重载时多增加一个int类型的参数,但调用函数时该参数不用传递,编译器自动传 递
- 后置++:注意:后置++是先使用后+1,因此需要返回+1之前的旧值,故需在实现时需要先将this保存一份, 然后给this+1 而temp是临时对象,因此只能以值的方式返回,不能返回引用。
我的花一定会开的。