文章目录
基础框架
Date.h
其实因为日期类的成员变量都是内置数据类型,所以拷贝构造和析构都可以不写,但养成习惯还是可以写的,尽量写一下比较好
#pragma once
#include<iostream>
#include<assert.h>
using namespace std;
class Date
{
public:
//构造函数
//注意:缺省只写在声明
Date(int year = 2023, int month = 2, int day = 4);
//拷贝构造函数
Date(const Date&d1);
private:
int _year;
int _month;
int _day;
};
Date.cpp
#include"Date.h"
//构造函数
Date::Date(int year, int month, int day)
{
assert(month>0&&month < 13 || day <= GetMonthDay(year, month)&&day>0);
_year = year;
_month = month;
_day = day;
}
//拷贝构造函数
Date::Date(const Date&d1)
{
_year = d1._year;
_month = d1._month;
_day = d1._day;
}
注意:
1.构造函数的缺省值,只写在声明就好
2.构造函数需要检查传入的参数是否合理
比较运算符重载
首先我们实现一些比较的运算符
比如:==,!=,<,>等
因为上篇运算符重载的博文已写过一遍,这里接直接上代码了
Date.h
//==运算符重载
bool operator==(const Date&d);
//d1<d2
bool operator<(const Date&d);
//d1<=d2
bool operator<=(const Date&d);
//d1>d2
bool operator>(const Date&d);
//d1>=d2
bool operator>=(const Date&d);
//d1!=d2
bool operator!=(const Date&d);
Date.cpp
//==运算符重载
bool Date::operator==(const Date&d)
{
return _year == d._year&&_month == d._month&&_day == d._day;
}
//d1<d2
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;
}
}
//d1<=d2
bool Date::operator<=(const Date&d)
{
return *this < d || *this == d;
}
//d1>d2
bool Date::operator>(const Date&d)
{
return !(*this <= d);
}
//d1>=d2
bool Date::operator>=(const Date&d)
{
return !(*this < d);
}
//d1!=d2
bool Date::operator!=(const Date&d)
{
return !(*this == d);
}
比较运算符,在写完"==“和”<"时,再往下写,我们发现会有很多重复的代码,那么此时,我们就可以利用已写的运算符重载,实现代码的复用
赋值&递增递减运算符重载
1.赋值运算符重载
赋值运算符也较为简单,直接上代码
Date& Date::operator=(const Date&d)
{
if (*this != d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
return *this;
}
需要注意的时,可以会出现d1=d1的情况,所以我们可以在函数内部判断一下
2.递增递减运算符重载
一. 前置++和后置++
++和–函数实际是不需要参数的,但因为编译器需要区分前置和后置,所以设定了一个区分标准:
如果参数列表没有参数,则为前置,如果有int,则为后置
具体代码如下
Date.h
//默认是前置++
Date& operator++();
//参数列表有整型为后置++
Date operator++(int);
Date.cpp
Date& Date::operator++()
{
*this += 1;
return *this;
}
Date Date::operator++(int)
{
Date tmp = *this;
*this += 1;
return tmp;
}
因为后置++,是进行其他操作然后再++,则我们需要返回++前的Date,所以我们在函数内部创建局部变量保存++前的数据,并且返回。但因为是局部变量,所以返回值不能是引用
二. 前置–和后置–
原理同+
//默认是前置--
Date& Date::operator--()
{
*this -= 1;
return *this;
}
//参数列表有整型为后置--
Date Date::operator--(int)
{
Date tmp = *this;
*this -= 1;
return tmp;
}
算术运算符重载
1.+&+=
因为日期加天数才有意义,所以我们设置参数是int
同时,再加完天数后,我们需要考虑是否要在月份进位,甚至年份进位。所以我们需要一个函数来获取当年当月的天数,来判断是否需要进位
Date.cpp
int Date::GetMonthDay(int year,int month)
{
int Month[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 };
//闰年的2月有29天
if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0)
{
Month[2] = 29;
}
return Month[month];
}
+运算重载代码如下
注意:因为是+,所以原本的对象数据并不会改变,所以我们需要创建局部变量来进行操作并返回,因为是局部变量,所以返回值不能是引用
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)
{
//普通写法
_day += day;
while (_day > GetMonthDay(_year ,_month))
{
_day -= GetMonthDay(_year, _month);
_month++;
if (_month == 13)
{
_year++;
_month = 1;
}
}
return *this;
}
那么这时候,我们发现两个函数的代码,有一段几乎相同,那么我们可考虑一方复用另一方
第一种:+复用+=
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;
}
Date Date::operator+(int day)
{
//复用+=运算符重载
Date tmp = *this;
tmp += day;
return tmp;
}
第二种:+=复用+
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)
{
//复用+运算符重载
*this = *this + day;
return *this;
}
两种方法都可以完成功能,但二者的效率是有所差距的,第一种方法是更好的。
因为我们发现,+两种方法都需要完成两次拷贝构造,所以效率相同,但是第二种方法的+=需要调用+和=,但第一种不需要,所以第一种效率更高
2.-和-=
-的逻辑和+差不多,不过-需要向月和年借天数。
-=运算符重载如下
//d1-=100
Date& Date::operator-=(int day)
{
_day -= day;
while (_day <= 0)
{
_month--;
if (_month == 0)
{
_year--;
_month = 12;
}
_day += GetMonthDay(_year, _month);
}
return *this;
}
-运算符重载如下
//d1-100
Date Date::operator-(int day)
{
Date tmp = *this;
tmp -= day;
return tmp;
}
但这时,我们发现一个问题,可能出现d1-= -100
这时,我们意识到,+和-的传参还需要分类正负
负负得正
Date& Date::operator+=(int day)
{
if (day < 0)
{
*this -= -day;
return *this;
}
_day += day;
while (_day > GetMonthDay(_year ,_month))
{
_day -= GetMonthDay(_year, _month);
if (_month == 13)
{
_year++;
_month = 1;
}
}
return *this;
}
Date& Date::operator-=(int day)
{
if (day < 0)
{
*this += -day;
return *this;
}
_day -= day;
while (_day <= 0)
{
_month--;
if (_month == 0)
{
_year--;
_month = 12;
}
_day += GetMonthDay(_year, _month);
}
return *this;
}
3.日期 — 日期
日期减日期,应当返回两个日期之间相差的天数,可能出现正数或者负数
//日期减日期
int Date::operator-(const Date&d)
{
//判断哪个日期比较大
Date max = *this;
Date min = d;
int flag = 1;//默认是正的
if (*this < d)
{
max = d;
min = *this;
flag = -1;
}
//n记录天数
int n = 0;
//max!=min的效率比min<max高
while (max != min)
{
//前置效率更高
++min;
n++;
}
return n*flag;
}
流插入重载&流提取重载
cout和cin是iostream的两个类,cout在ostream,cin在istream。
对于内置数据类型,iostream头文件内是已经编写的
比如:
int a=0;
double b=1.11;
//cout.operator(a)
cout<<a<<endl;
//cout.operator(b)
cout<<b<<endl;
那么对于自定义数据类型,我们则需要自己编写
1.流插入重载
首先,如果我们编写在类内
//流插入重载
void Date::operator<<(ostream&out)
{
out << _year << "年" << _month << "月" << _day << "日" << endl;
}
int main()
{
Date d1(2023,2,8);
//那么调用会是这样的
//d1.operator<<(cout);
d1<<cout;//不符合常规书写
}
我们发现函数调用时,书写形式和我们平常书写的不一样。
所以,流插入重载我们不能写在类内。
那如果设为全局函数,又无法访问私有成员变量,将私有改成共有显然是不合适的,我们其实可以在类内定义获取私有成员变量的函数,比如GetYear,GetMonth等等,但C++更常用友元解决这个问题
一.友元
可以让类外函数访问私有属性
class Date
{
//友元 关键字friend
friend void operator<<(ostream&out, const Date&d);
public:
//构造函数
Date(int year = 2023, int month = 2, int day = 4);
//拷贝构造函数
Date(const Date&d1);
private:
int _year;
int _month;
int _day;
};
//流插入重载
void operator<<(ostream&out, const Date&d)
{
out << d._year << "年" << d._month << "月" << d._day << "日" << endl;
}
然后,我们发现,会有cout<<d1<<d2<<endl;的情况,所以返回值我们应该设为ostream,可以继续调用重载。
又因为cout是全局,并且只能有一个,不能拷贝,所以需要加&
ostream& operator<<(ostream&out, const Date&d)
{
out << d._year << "年" << d._month << "月" << d._day << "日" << endl;
return out;
}
2.流提取
代码如下
//流提取重载
istream& operator>>(istream&in, Date&d)
{
in >> d._year >> d._month >> d._day;
return in;
}
因为cin是要对对象进行修改的,所以不能加const。
内联函数的补充知识
在类内定义的成员函数,编译器可能会将其定为内联函数(看其是否经常调用且短小)
如果我们将流插入和流提取设置为内联函数,声明在Date.h中
会出现链接错误。
因为在Date.h中找这两个函数,发现是内联函数,就直接展开了,没有进行Date.cpp链接,所以函数调用失败。所以我们直接在Date,h中完成定义
结束语
本章用于记笔记,如果有不对或者不足的地方,欢迎大佬们指正,补充。感谢大家的阅读,如果感觉博主写的还可以,麻烦点个赞支持一下,阿里嘎多。