目录
1.介绍
2.计算往后day天
2.1逻辑分析
2.2代码优化
3.计算往前day天
3.1代码分析
3.2遇到的问题
3.3补充逻辑
4.补充++运算符重载
5.'<<' 流插入和 '>>' 流提取
我将通过不断添加函数的方法讲解如何实现,和改进前面的函数,也对之前讲的进行补充和改进,更好的理解类和对象,尽量用最少的字来描述清楚
class Date
{
public:
//默认构造
Date(int year = 2024, int month = 1, int day = 29)
{
assert(month > 0 && month < 13 && day <= GetMonthDay(year, month));
_year = year;
_month = month;
_day = day;
}
//拷贝构造
Date(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
//赋值运算符重载
Date& operator=(const Date& d)//赋值运算符重载
{
_year = d._year;
_month = d._month;
_day = d._day;
return *this;
}
//析构函数
~Date()
{
_year = 0;
_month = 0;
_day = 0;
}
private://声明 main里Date d1; 是定义(对象整体的定义)
int _year;
int _month;
int _day;
};
对于这Date类(没有堆空间开辟),这里的四个都可以不写(编译器会自己生成),默认构造里的assert可以忽略
注意:赋值和取地址运算符是唯一可以直接调用的,因为它们是默认成员函数,像比较运算符,+,+=,++这些要自己写(下文解释)
class Date
{
public:
int GetMonthDay(int year, int month)
{
assert(month > 0 && month < 13);
int array[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 array[month];
}
Date& operator+=(int day)//date += 100;
{
_day += day;
while (_day > GetMonthDay(_year, _month))
{
_day -= GetMonthDay(_year, _month);
_month++;
if (_month == 13)
{
_year++;
_month = 1;
}
}
return *this;
}
Date operator+(int day) const
{
Date tmp = *this;//拷贝构造
tmp += day;
return tmp;//传值返回,拷贝构造
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1(2024, 1, 30);
d1 += 1000;
return 0;
}
还没有写 '<<' 流插入运算符重载,就到监视窗口看
和日期计算器里的一样
1.GetMonthDay这个函数,唯一区别是一个闰年的29天,那么可以把其他月天数放在数组里
2.+=是要改变*this的所以直接操作成员变量,考虑到赋值运算符的连续性,return *this,出了函数*this还存在,那么传引用返回
3.+的话,是不改变自己,那么就实例化一个对象出来,会调用两次拷贝构造,但是我在调试的时候发现,传值返回没有走到拷贝构造里,查阅资料发现 简单来说这是编译器的优化,但是在逻辑上仍然要返回一个Date类型的临时对象
1.我已经是优化好了的(逻辑还差一点,day是负的),用+=去复用+可以减少拷贝构造
2.发现我在operator+这个函数后面加了const,这个是对*this的修饰,
补全这样:const Date* this
就是不能修改*this了,我第一次讲的不到位(直接理解const),其实我们不需要自己增加任何规则:
说白了无非是两种①const Date* this ② Date* const this;
那么看:*this就是对象,放在C里说就是变量,那么const修饰了这个变量,变量不能变;
this是指针,const修饰了指针,指针不能变
class Date
{
public:
int GetMonthDay(int year, int month)
{
assert(month > 0 && month < 13);
int array[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 array[month];
}
Date& operator-=(int day)
{
_day -= day;
while (_day <= 0)//<=0去上个月借
{
//法一先改_day
//if(_month > 1)
// _day += GetMonthDay(_year, _month - 1);
//if (_month == 1)//单独拿出来,一月去借12月的
// _day += 31;
//_month--;
//if (_month == 0)
//{
// _year--;
// _month = 12;
//}
//法二先改_month
_month--;
if (_month == 0)
{
_month = 12;
_year--;
}
_day += GetMonthDay(_year, _month);
}
return *this;
}
Date operator-(int day)//运损符重载又可以构成函数重载
{
Date tmp = *this;
tmp -= day;
return tmp;
}
int operator-(const Date& d)const
{
//法一
/*int n = 0;
Date left = *this;
Date right = d;
if (left > right)
{
while (left > right)
right++, n++;
return n;
}
else
{
while (left < right)
left++, n++;
return -n;
}*/
//法二
Date max = *this;
Date min = d;
int flag = 1;
//☆☆☆if (*this < d)///因为上面的< 最操作数没加const☆☆☆①
if(max < min)
{
max = d;
min = *this;
flag = -1;
}
int n = 0;
while (min < max)
{
++min;
++n;
}
return n * flag;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1(2024, 1, 30);
d1 -= 1000;
return 0;
}
运行结果没问题
这里有个 ‘-’ 减号运算符重载
①日期 = 日期 - 天数
②天数 = 日期 - 日期
①这句代码:if(max < min),我第一次是这么写的:if(*this < d),出现没有与这些操作数匹配的 "<" 运算符,我前面不是写了吗?问题出在这:我的函数声明,int operator-(const Date& d)const
后面要写const,为什么后面写??主要是为了防止两个const对象没法调用
也就是*this是const,但是我写的 :bool operator<(const Date& d),这个声明是左参数不是const,所以发生权限的放大,也就是类型的不匹配
改进比较运算符函数重载就是在声明后面加上const,避免找不到对应的函数重载
Date& operator+=(int day)//date += 100;
{
if (day < 0)
{
*this -= -day;
return *this;
}
//...
}
Date& operator-=(int day)
{
if (day < 0)
{
*this += -day;
return *this;
}
//...
}
往前走负的,就是往后多少天;往后走负的,就是往前多少天
有没有人会有这样的问题:比如1.30前面的30天就是1.10号吗?是9?10?11?
①昨天就是当天-1!
②这样清楚吧 ,1.29的前5天
结论:所以直接减就行了
//前置++
Date& operator++()
{
*this += 1;
return *this;
}
//后置++
Date operator++(int)//站位,与前置++构成运算符重载
{
Date tmp = *this;//拷贝构造
*this += 1;
return tmp;//拷贝构造//调试的时候没有
}
后置++里面的int 启到站位作用,没有实际意义,为了构成重载,可以不写形参
这里的传引用返回和传值返回应该是没问题了吧
如果我们写在Date类里面
void operator<<(ostream& out)//传进来的参数是cout//日期类对象*this抢占了第一个参数
{
}
那我们调用的时候就要这么写:d1< 没有,因为第一个参数一定是*this,在调用的时候是这样的d1< 所以只能写在全局 但是还是不对_year,_month,_day是私有成员变量,类外无法访问 法一:函数里增加GetYear()这类函数 法二:友元函数,在类里加上friend ostream& operator<<(ostream& out, const Date& d);即可,格式为 friend+函数声明 + ; 注:out就是传进来的cout对象 问题①:把out,都改成cout会调用std库里的cout吗? 不会,因为局部优先,这只是个形参,但是这个形参就是std::cout 问题②:为什么ostream&这么传返回值,其实返回的就是std::cout,为了连续输出 这部分细节比较多,还是要查阅资料,一下讲完和标题不匹配了,[]运算符,内联,初始列表,static,内部类下一部分举例子,放平心一定能学会的,我也花了很长时间,也是初学者,讲的不一定对,带着怀疑看吧!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;
}
//显示调用
operator<<(cout, d1);
//隐式调用
cout << d1;