【C++】:拷贝构造函数与赋值运算符重载的实例应用之日期类的实现
C++实现日期类
├─属性:
│ ├─年份
│ ├─月份
│ └─日期
├─方法:
│ ├─构造函数
│ ├─拷贝构造函数
│ ├─析构函数
│ ├─设置年份
│ ├─设置月份
│ ├─设置日期
│ ├─获取年份
│ ├─获取月份
│ ├─获取日期
│ ├─判断是否为闰年
│ ├─计算该日期是该年的第几天
│ ├─计算该日期是星期几
│ └─重载运算符(+、-、==、!=、<、>、<=、>=)
一、📚头文件的声明(Date.h)
🔑日期类的实现,将按以下声明依次进行,其中因为Print函数比较短,直接放到类里面让其变成内联函数
#pragma once
#include<iostream>
#include<assert.h>
using namespace std;
class Date
{
public:
Date(int year = 1, int month = 1, int day = 1);
void Print();
int GetMonthDay(int year, int month);
bool operator==(const Date& y);
bool operator!=(const Date& y);
bool operator>(const Date& y);
bool operator<(const Date& y);
bool operator>=(const Date& y);
bool operator<=(const Date& y);
int operator-(const Date& d);
Date& operator+=(int day);
Date operator+(int day);
Date& operator-=(int day);
Date operator-(int day);
Date& operator++();
Date operator++(int);
Date& operator--();
Date operator--(int);
private:
int _year;
int _month;
int _day;
};
class Date
{
public:
// 获取某年某月的天数
int GetMonthDay(int year, int month)
{
static int days[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30,
31};
int day = days[month];
if (month == 2
&&((year % 4 == 0 && year % 100 != 0) || (year%400 == 0)))
{
day += 1;
}
return day;
}
// 全缺省的构造函数
Date(int year = 1900, int month = 1, int day = 1);
// 拷贝构造函数
// d2(d1)
Date(const Date& d);
// 赋值运算符重载
// d2 = d3 -> d2.operator=(&d2, d3)
Date& operator=(const Date& d);
// 析构函数
~Date();
// 日期+=天数
Date& operator+=(int day);
// 日期+天数
Date operator+(int day);
// 日期-天数
Date operator-(int day);
// 日期-=天数
Date& operator-=(int day);
// 前置++
Date& operator++();
// 后置++
Date operator++(int);
// 后置--
Date operator--(int);
// 前置--
Date& operator--();
// >运算符重载
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);
// 日期-日期 返回天数
int operator-(const Date& d);
private:
int _year;
int _month;
int _day;
};
二、📚获取天数的函数
🔑而我们实现日期类经常要用到某月有多少天,在这里先把获得某月有多少天的函数实现出来。实现时先检查传参有没有问题,在注意把数组设置成静态的,出了作用域还能访问,就不需要考虑每次调用函数建立栈帧后重新给数组分配空间的事情了,因为数组一直被存放在静态区 其次我们先判断这个月是不是二月份,避免判断某年是平年还是闰年一大堆操作后,发现月份不是二月份
int Date::GetMonthDay(int year, int month)
{
assert(year >= 1 && month >= 1 && month <= 12);
static int monthArray[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;
return monthArray[month];
}
三、📚Date的默认成员函数
🔑1.编译器默认生成的构造函数不会处理内置类型,所以我们需要自己去写构造函数,推荐全缺省的构造函数,编译器对自定义类型会自动调用该类型的默认构造
🔑2.由于Date类的成员变量都是内置类型,所以析构函数不需要我们自己写,因为没有资源的申请。并且拷贝构造和赋值重载也不需要写,因为Date类不涉及深拷贝的问题,仅仅使用浅拷贝就够了
Date::Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
if (_year < 1 ||
_month < 1 || _month > 12 ||
_day < 1 || _day > GetMonthDay(_year, _month))
{
//assert(false);
Print();
cout << "日期非法" << endl;
}
}
🔑用一个类初始化另外一个类
Date::Date(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
四、📚日期类的大小比较
🔑由于日期类的大小比较,均不涉及对自身的改变,对此,我们统一用const来修饰this指针,让其变成const成员函数,减少代码的出错性
五、📚>运算符重载
🔑在这里我们找出所有日期a大于日期b的情况 第一种:年比年大 第二种:年相同 月比月大 第三种:年和月都相同 日比日大 再依次向下写就完成了>的比较
bool Date::operator>(const Date& y)
{
if (_year > y._year)
{
return true;
}
else if (_year == y._year && _month > y._month)
{
return true;
}
else if (_year == y._year && _month == y._month && _day > y._day)
{
return true;
}
return false;
}
六、📚==运算符重载
bool Date::operator==(const Date& y)
{
return _year == y._year
&& _month == y._month
&& _day == y._day;
}
七、📚>= < <= !=对> ==的复用
// d1 != d2
bool Date::operator!=(const Date& y)
{
return !(*this == y);
}
bool Date::operator>(const Date& y)
{
if (_year > y._year)
{
return true;
}
else if (_year == y._year && _month > y._month)
{
return true;
}
else if (_year == y._year && _month == y._month && _day > y._day)
{
return true;
}
return false;
}
bool Date::operator>=(const Date& y)
{
return *this > y || *this == y;
}
bool Date::operator<(const Date& y)
{
return !(*this >= y);
}
bool Date::operator<=(const Date& y)
{
return !(*this > y);
}
八、📚日期类的计算
🔑日期类的连续赋值
在内置类型的适合我们经常有连续赋值的习惯,类似a1=a2=a3这种,而日期类也支持连续赋值的操作对此我们返回值不能写void 而应该返回引用,我们可以减少拷贝,从而提高效率 这是一名C/C++程序员的基本素养
Date& Date::operator=(const Date& d)
{
this->_year = d._year;
this->_month = d._month;
this->_day = d._day;
return *this;
}
🔑日期类的加法
+=运算符重载和+对+=的复用
🔑+=实现的思路就是,实现一个循环,直到天数回到该月的正常天数为止,在循环内部要做的就是进月和进年,让天数不断减去本月天数,直到恢复本月正常天数时,循环结束,返回对象本身即可
// 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;
}
Date Date::operator+(int day)
{
Date tmp(*this);
tmp += day;
return tmp;
}
🔑-=实现的思路就是,实现一个循环,直到天数变为正数为止,在循环内部要做的就是借月和借年,让天数不断加上上一个月份的天数,直到恢复正数为止,循环结束,返回对象本身
Date& Date::operator-=(int day)
{
if (day < 0)
{
return *this += (-day);
}
_day -= day;
while (_day <= 0)
{
--_month;
if (_month == 0)
{
--_year;
_month = 12;
}
_day += GetMonthDay(_year, _month);
}
return *this;
}
Date Date::operator-(int day)
{
Date tmp(*this);
tmp -= day;
return tmp;
}
🔑前置++和后置++重载
后置++比前置++多一个参数int 同时后置返回的临时变量 不能添加引用 同时两个this都被改变了 不加const修饰
++d1;
d1.operator++();
d1.Print();
d1++;
d1.operator++(10);
d1.operator++(1);
d1.Print();
// ++d1
Date& Date::operator++()
{
*this += 1;
return *this;
}
// d1++
Date Date::operator++(int)
{
Date tmp(*this);
*this += 1;
return tmp;
}
🔑前置−−和后置−−重载
Date& Date::operator--()
{
*this -= 1;
return *this;
}
Date Date::operator--(int)
{
Date tmp(*this);
*this -= 1;
return tmp;
}
九、📚日期−日期重载
🔑日期类相减不需要日期本身,因此用const修饰。由于采用运算法求日期减去日期比较麻烦 还好考虑差有几年 几月 甚至几月中是否包括二月 所以在这样我们采用小日期自增的方式实现 用一个变量n记录
// d1 - d2
int Date::operator-(const Date& d)
{
// 假设左大右小
int flag = 1;
Date max = *this;
Date min = d;
// 假设错了,左小右大
if (*this < d)
{
max = d;
min = *this;
flag = -1;
}
int n = 0;
while (min != max)
{
++min;
++n;
}
return n * flag;
}
十、📚日期类实现总代码
#include "Date.h"
Date::Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
if (_year < 1 ||
_month < 1 || _month > 12 ||
_day < 1 || _day > GetMonthDay(_year, _month))
{
//assert(false);
Print();
cout << "日期非法" << endl;
}
}
void Date::Print()
{
cout << _year << "/" << _month << "/" << _day << endl;
}
bool Date::operator==(const Date& y)
{
return _year == y._year
&& _month == y._month
&& _day == y._day;
}
// d1 != d2
bool Date::operator!=(const Date& y)
{
return !(*this == y);
}
bool Date::operator>(const Date& y)
{
if (_year > y._year)
{
return true;
}
else if (_year == y._year && _month > y._month)
{
return true;
}
else if (_year == y._year && _month == y._month && _day > y._day)
{
return true;
}
return false;
}
bool Date::operator>=(const Date& y)
{
return *this > y || *this == y;
}
bool Date::operator<(const Date& y)
{
return !(*this >= y);
}
bool Date::operator<=(const Date& y)
{
return !(*this > y);
}
int Date::GetMonthDay(int year, int month)
{
assert(year >= 1 && month >= 1 && month <= 12);
int monthArray[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;
return monthArray[month];
}
// 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;
}
Date Date::operator+(int day)
{
Date tmp(*this);
tmp += day;
return tmp;
}
//
// d1 += 100
//Date& Date::operator+=(int day)
//{
// //Date d = *this + day;
// //*this = d;
//
// *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)
{
if (day < 0)
{
return *this += (-day);
}
_day -= day;
while (_day <= 0)
{
--_month;
if (_month == 0)
{
--_year;
_month = 12;
}
_day += GetMonthDay(_year, _month);
}
return *this;
}
Date Date::operator-(int day)
{
Date tmp(*this);
tmp -= day;
return tmp;
}
// 21:13继续
// ++d1
Date& Date::operator++()
{
*this += 1;
return *this;
}
// d1++
Date Date::operator++(int)
{
Date tmp(*this);
*this += 1;
return tmp;
}
Date& Date::operator--()
{
*this -= 1;
return *this;
}
Date Date::operator--(int)
{
Date tmp(*this);
*this -= 1;
return tmp;
}
// d1 - d2
int Date::operator-(const Date& d)
{
// 假设左大右小
int flag = 1;
Date max = *this;
Date min = d;
// 假设错了,左小右大
if (*this < d)
{
max = d;
min = *this;
flag = -1;
}
int n = 0;
while (min != max)
{
++min;
++n;
}
return n * flag;
}
#pragma once
#include<iostream>
#include<assert.h>
using namespace std;
class Date
{
public:
Date(int year = 1, int month = 1, int day = 1);
void Print();
int GetMonthDay(int year, int month);
bool operator==(const Date& y);
bool operator!=(const Date& y);
bool operator>(const Date& y);
bool operator<(const Date& y);
bool operator>=(const Date& y);
bool operator<=(const Date& y);
int operator-(const Date& d);
Date& operator+=(int day);
Date operator+(int day);
Date& operator-=(int day);
Date operator-(int day);
Date& operator++();
Date operator++(int);
Date& operator--();
Date operator--(int);
private:
int _year;
int _month;
int _day;
};
#include "Date.h"
void TestDate1()
{
Date d1(2023, 10, 24);
d1.Print();
Date ret1 = d1 - 100;
ret1.Print();
Date ret2 = d1 - 10000;
ret2.Print();
Date ret3 = d1 + 100;
ret3.Print();
Date ret4 = d1 + 10000;
ret4.Print();
}
void TestDate2()
{
Date d1(2023, 10, 24);
d1.Print();
// 语法设计,无法逻辑闭环,那么这时就只能特殊处理
// 特殊处理
++d1;
d1.operator++();
d1.Print();
d1++;
d1.operator++(10);
d1.operator++(1);
d1.Print();
}
void TestDate3()
{
Date d1(2023, 10, 24);
d1.Print();
Date d2(2024, 5, 5);
d2.Print();
Date d3(2024, 8, 1);
d3.Print();
cout << d2 - d1 << endl;
cout << d1 - d3 << endl;
}
void TestDate4()
{
Date d1(2023, 10, 24);
d1 += -100;
d1.Print();
}
int main()
{
TestDate4();
return 0;
}