代码
Date.h
#pragma once
#include <iostream>
using namespace std;
class Date
{
friend ostream& operator<<(ostream& out, const Date& d);
friend istream& operator>>(istream& in, Date& d);
public:
Date(int _year = 0, int _month = 0, int _day = 0); // 建议将默认参数放到声明中(不能声明和定义都放,会造成重定义默认参数),放到定义中可能会导致错误"没有合适的默认构造函数可用"
void print();
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);
Date& operator=(const Date& d);
Date& operator+=(int d);
Date operator+(int d);
Date& operator-=(int d);
Date operator-(int d);
Date& operator++();
Date operator++(int);
Date& operator--();
Date operator--(int);
private:
int is_leap(int _month);
int getDays(int _year, int _month);
int _year;
int _month;
int _day;
};
Date.cpp
#include "Date.h"
Date::Date(int _year, int _month, int _day) // 在声明中设置了参数默认值,定义就不能设置了,否则报出“重定义缺省参数”
{
this->_year = _year;
this->_month = _month;
this->_day = _day;
}
void Date::print()
{
cout << this->_year << '-' << this->_month << '-' << this->_day << endl;
}
int Date::is_leap(int _year)
{
if (_year % 4 == 0 && _year % 100 != 0 || _year % 400 == 0)
{
return 1;
}
return 0;
}
int Date::getDays(int _year, int _month)
{
int months[] = { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
return (_month == 2 ? months[_month] + is_leap(_year) : months[_month]);
}
bool Date::operator<(const Date& d)
{
if (d._year != this->_year)
{
return this->_year < d._year;
}
if (d._month != this->_month)
{
return this->_month < d._month;
}
if (d._day != this->_day)
{
return this->_day < d._day;
}
return false; // 补充代码
}
bool Date::operator<=(const Date& d)
{
/*if (d._year != this->_year)
{
return this->_year < d._year;
}
if (d._month != this->_month)
{
return this->_month < d._month;
}
if (d._day != this->_day)
{
return this->_day < d._day;
}
return true;*/
return !(*this > d);
}
bool Date::operator>(const Date& d)
{
if (d._year != this->_year)
{
return this->_year > d._year;
}
if (d._month != this->_month)
{
return this->_month > d._month;
}
if (d._day != this->_day)
{
return this->_day > d._day;
}
return false; // 补充代码
}
bool Date::operator>=(const Date& d)
{
/*if (d._year != this->_year)
{
return this->_year < d._year;
}
if (d._month != this->_month)
{
return this->_month < d._month;
}
if (d._day != this->_day)
{
return this->_day < d._day;
}
return true;*/
return !(*this < d);
}
bool Date::operator==(const Date& d)
{
return d._year == this->_year
&& d._month == this->_month
&& d._day == this->_day;
}
bool Date::operator!=(const Date& d)
{
return !(*this == d);
}
Date& Date::operator=(const Date& d)
{
if (*this != d)
{
this->_year = d._year;
this->_month = d._month;
this->_day = d._day;
}
return *this;
}
Date& Date::operator+=(int d) // 如果是我难以使用到这么简便的代码
{
if (d < 0) // 针对d为负数的情况
{
return (*this -= -d);
}
this->_day += d;
while (this->_day >= getDays(this->_year, this->_month))
{
this->_day -= getDays(this->_year, this->_month);
this->_month++;
if (this->_month > 12)
{
this->_month = 1;
this->_year++;
}
}
return *this;
}
Date Date::operator+(int d) // 加法不应该影响调用它的对象,需要创建一个临时变量,所以返回值类型不能是引用
{
if (d < 0)
{
return (*this - -d);
}
Date tmp(*this);
tmp._day += d;
while (tmp._day >= getDays(tmp._year, tmp._month))
{
tmp._day -= getDays(tmp._year, tmp._month);
tmp._month++;
if (tmp._month > 12)
{
tmp._month = 1;
tmp._year++;
}
}
return tmp;
}
Date& Date::operator-=(int d)
{
if (d < 0) // 针对d为负数的情况
{
return (*this += -d);
}
this->_day -= d;
//while (this->_day <= 0)
//{
// this->_day += getDays(this->_year, --this->_month); // 不能和+=相同
// if (this->_month == 0)
// {
// this->_month = 12;
// this->_year--;
// }
//}
while (this->_day <= 0)
{
if (--this->_month == 0)
{
this->_month = 12;
this->_year--;
}
this->_day += getDays(this->_year, this->_month);
}
return *this;
}
Date Date::operator-(int d)
{
if (d < 0) // 针对d为负数的情况
{
return (*this + -d);
}
Date tmp(*this);
tmp._day -= d;
while (tmp._day <= 0)
{
if (--tmp._month == 0)
{
tmp._month = 12;
tmp._year--;
}
tmp._day += getDays(tmp._year, tmp._month);
}
return tmp;
}
Date& Date::operator++()
{
return (*this += 1);
}
Date Date::operator++(int)
{
Date tmp(*this);
++(*this);
return tmp;
}
Date& Date::operator--()
{
return (*this -= 1);
}
Date Date::operator--(int)
{
Date tmp(*this);
--(*this);
return tmp;
}
test.cpp
#include "Date.h"
ostream& operator<<(ostream& out, const Date& d)
{
out << d._year << '-' << d._month << '-' << d._day;
return out;
}
istream& operator>>(istream& in, Date& d)
{
in >> d._year >> d._month >> d._day;
return in;
}
int main()
{
Date d1;
cin >> d1; // 2022 1 1
Date d2 = d1;
Date d3;
d2 = d3 = d1;
d1 -= 10;
cout << d1 << endl;
d1--;
cout << d1 << endl;
d1++;
cout << d1 << endl;
d1 += 10;
cout << d1 << endl;
d1 -= -100;
cout << d1 << endl;
d1 = d1 - -100;
cout << d1 << endl;
d1 += -100;
cout << d1 << endl;
d1 = d1 + -100;
cout << d1 << endl;
cout << (d1 > d2) << endl;
cout << (d1 >= d2) << endl;
cout << (d1 <= d2) << endl;
cout << (d1 < d2) << endl;
cout << (d1 == d2) << endl;
cout << (d1 != d2) << endl;
d3 = d1 + 10;
cout << d3 << endl;
d3 = d1 - 10;
cout << d3 << endl;
return 0;
}
运行结果
分析
-
对于构造函数中的缺省参数的设置:如果是声明与定义分开写的话,缺省参数只能设置在声明中,否则,报出
重定义缺省参数
。 -
print
函数其实可以被流插入运算符重载替换。
流插入和流提取运算符重载,实现成全局函数更合适一些。
因为实现成成员变量,函数参数列表处this
会默认排在第一个,导致只有对象 << cout;
或对象 >> cin;
时,才能正常进行输入输出,导致操作别扭。但是,将这两个函数设置成成员变量,也有个阻碍就是,全局函数不像成员函数一样,无法访问对象的私有或保护成员变量或函数。此时,将这两个运算符重载函数设置成友元函数即可
-
返回值引用使用在了:=重载、+=重载、-=重载、前置++重载、后置–重载、流插入重载、流提取重载;
原因:
- 允许链式操作。(尤其对于=、+=、-=)
a = b = c
实际情况是:a = (b = c)
,b = c
的返回结果是b
。
如果返回临时对象,a = b
操作开始前,该临时对象的生命周期已经结束。无法完成链式赋值操作。但是我在vs2022中为value返回值时,可以正常链式赋值,可能时编译器进行了优化吧(留有疑问)。 - 返回引用,不需要调用构造函数和析构函数。提升了效率。
- 允许链式操作。(尤其对于=、+=、-=)
-
在实现函数重载时,其实可以提高代码的复用。
比如==
就是!(!=)
,>=
就是!<
,++
就是+= 1
等等诸如此类。 -
+=、-=、-、+运算符重载时的代码陷阱
因为默认这些操作时的操作数都是正数,忽略了操作数为负数的情况。导致代码逻辑出现了bug。解决:使用
if
判断,让负数跳转到合适的函数中即可。 -
赋值运算符重载时,防止自我赋值。
防止自我赋值的意义是很大的,首先,为了效率毋庸置疑。其次,如果成员变量中存在属性在堆中,在进行赋值前,一定先进行地是,释放堆中资源,防止内存泄漏,此时进行自我赋值,则会导致数据丢失。 -
将只在成员函数中使用的两个成员函数(
is_leap()``getDays()
)的权限设置成私有,为了更好的封装性。 -
前置++、-- 和 后置++、–
实际上,运算符重载时的函数名只有:operator运算符
这一种形式。那么如何对前置和后置运算符进行区分呢?C++设计者使用了整型
占位符进行区分:即在后置运算符的形参列表中加入int
。
前置运算符返回值可以是引用(返回的是操作数本身),而后置运算符返回值不可以是引用(返回的是重载函数中的局部变量)。这和前置后置操作的逻辑有关。