C++项目实战(日期类的实现)
🎈 个人主页👉:tbRNA-CSDN博客tbRNA-CSDN博客tbRNA-CSDN博客
💯 个人简介:在校大学生一枚💋.
😍 希望我的文章对大家有着不一样的帮助,欢迎大家关注我,感谢大家的多多支持!🎉 欢迎 👍点赞 ✍评论 ⭐收藏
类与对象往期文章👇
C++基础知识点(五)
C++基础知识点(四)
C++基础知识点(三)
C++基础知识点(二)
C++基础知识点(一)
目录
第一部分: Date.h
第二部分: Date.cpp
第三部分: Test.cpp
第四部分:总结
☺️ 今天带来一个日期类项目的实战,主要是针对类与对象这部分内容进行复习与总结。
日期类项目结构分为三个部分:
1. Date.h(函数的声明)
2. Date.cpp(函数的定义)
3. Test.cpp(主函数)
第一部分: Date.h
#pragma once#include <iostream>
#include <assert.h>
using namespace std;class Date
{
public:// 输入检查bool CheckOut();// 构造函数Date(int year, int month, int day);// 输出函数void Print();// 获取天数(默认是内联)int GetMonthDay(int year, int month){assert(month > 0 && month < 13);static int MonthDayArray[13] = { -1, 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 MonthDayArray[month];}// "+"的运算符重载Date operator+(int day);// "+="的运算符重载Date& operator+=(int day);// "-"的运算符重载Date operator-(int day);// "-="的运算符重载Date& operator-=(int day);// "后置++"的运算符重载Date operator++(int);// "前置++"的运算符重载Date& operator++();// "后置--"的运算符重载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 GetMonthDay(int year, int month) 这个函数因为需要频繁调用(获取当月天数),因此把他放在类内定义默认为内联函数(inline),这样调用函数的过程就可以直接执行函数代码,而不发生跳转、压栈等一般性函数操作,因此可以节省时间,也会提高程序的执行速度。
😛 复习一下内联函数(inline)
1. 内联函数的定义
inline 关键字是C99标准的型关键字,其作用是:
① 将函数展开,把函数的代码复制到每一个调用处。
② 这样调用函数的过程就可以直接执行函数代码,而不发生跳转、压栈等一般性函数操作。
③ 可以节省时间,也会提高程序的执行速度。
2. 内联函数的使用
① 关键字 inline 的使用是有所限制的:
inline 只适合函数体内代码比较简单的函数使用,不能包含复杂的结构控制语句,例如while、switch,并且内联函数本身不能是直接递归函数(函数内部调用自己的函数)。
② inline 仅是一个对编译器的建议:
inline 函数仅仅是一个对编译器的建议,所以最后能否真正内联,需要看编译器的意思,它如果认为函数不复杂,能在调用点展开,就会真正内联,并不是说声明了内联就会内联,声明内联只是一个建议而已。
③ 建议把 inline 函数的定义放在头文件中:
因为内联函数要在调用点展开,所以编译器必须随处可见内联函数的定义,要不然就成了非内联函数的调用了。
所以,这要求每个调用了内联函数的文件都出现了该内联函数的定义。
因此,将内联函数的定义放在头文件里实现是合适的,避免每个文件实现一次的麻烦。
④ inline 不建议声明和定义分离到两个⽂件,分离会导致链接错误。
因为inline被展开,就没有函数地址,链接时会出现报错,因此需要定义 inline函数 在头文件。
3. 内联函数的优缺点
😜优点: 普通函数在调用过程中,会对寄存器中内容进行上下文切换(比如push和pop操作),而内联函数则不需要,所以普通函数相比内联函数,耗时要多一些。
🫢 缺点:当函数使用次数比较多的时候,内联函数在每个调用的地方都会被展开,所以导致固件大小会变大,同一段代码会多次重复出现在固件中。而普通函数则没有此问题,不管调用的函数的次数多少,函数在固件中均只占用一处,空间利用率较高。
因此,inline函数其实就是空间换时间。
int GetMonthDay(int year, int month)
{assert(month > 0 && month < 13);static int MonthDayArray[13] = { -1, 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 MonthDayArray[month];
}
二. GetMonthDay函数好用的点在于:
1. 首先先对传入的month进行判断,避免输入错误。
2. 采用静态static创建一个月天数数组。
🤔 复习一下static:
1. static是静态修饰符,由其关键字修饰的变量会保存到全局数据区。
① 对于普通的局部变量或者全局变量,都是由系统自动分配内存的,并且当变量离开作用域的时候释放掉。
② 而使用 static 关键字来修饰,只有当程序结束时候才会释放掉。
那么static跟inline函数(内联)结合使用的好处是什么呢?
① 使用static inline修饰时,函数仅在文件内部可见,不会污染命名空间。
② 函数在运行过程中也会分配内存空间,但是由于static的存在(和修饰变量类似),它只会开辟一块内存空间。
😏 月天数数组的元素个数为13个,避免第0月的错误,保证了正确月份的顺序 。
对于闰年的2月份,则采用单独进行闰年判断处理:
if (month == 2 && ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)))return 29;
三. 将对象进行引用返回,减少值拷贝
// "+="的运算符重载
Date& operator+=(int day);
// "-="的运算符重载
Date& operator-=(int day);
// "前置++"的运算符重载
Date& operator++();
// "前置--"的运算符重载
Date& operator--();
第二部分: Date.cpp
#include "Date.h"bool Date::CheckOut()
{if (_month < 1 || _month > 12 || _day < 1 || _day > GetMonthDay(_year, _month)){return false;}else{return true;}
}Date::Date(int year, int month, int day)
{_year = year;_month = month;_day = day;if (!CheckOut()){cout << "非法日期:";Print();}
}void Date::Print()
{cout << _year << "-" << _month << "-" << _day << endl;
}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)
{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;
}Date Date::operator-(int day)
{Date tmp = *this;tmp -= day;return tmp;
}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;
}Date& Date::operator--()
{*this -= 1;return *this;
}bool Date::operator<(const Date& d)
{if (_year < d._year){return true;}else if (_year == d._year){if (_month < d._month){return true;}else if (_month == d._month){if (_day < d._day){return true;}}}return false;
}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 _year == d._year && _month == d._month && _day == d._day;
}bool Date::operator!=(const Date& d)
{return !(*this == d);
}
😋Date.cpp 这部分代码是对头文件声明的函数进行定义
我们可以看到:
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)
{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;
}Date Date::operator-(int day)
{Date tmp = *this;tmp -= day;return tmp;
}
🥸在对" += " 和 " + " ,以及 " -= " 和 " - " 这些运算符重载中,可以直接调用已定义的运算符进行重载,比如 " + " 运算符可以调用 " += " 运算符," -= " 运算符可以调用 " - " 运算符。
🥳相对于已经定义好的" + "和" - "," += " 也可以调用 " + "," -= " 也可以调用 " - " 。
bool Date::operator<(const Date& d)
{if (_year < d._year){return true;}else if (_year == d._year){if (_month < d._month){return true;}else if (_month == d._month){if (_day < d._day){return true;}}}return false;
}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 _year == d._year && _month == d._month && _day == d._day;
}bool Date::operator!=(const Date& d)
{return !(*this == d);
}int Date::operator-(const Date& d)
{Date max = *this;Date min = d;int flag = 1;if (*this < d){flag = -1;max = d;min = *this;}int n = 0;while (min != max){++min;++n;}return flag * n;
}
🤪同时从上面代码我们也可以看到,这么多函数定义中我们只需要详细写其中一两个,其他的函数定义都可以直接调用已定义的函数,实现了代码的简洁高效。
整个日期类实现中重点函数是:
1. 日期加天数
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;
}
2. 日期减天数
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;
}
3. 两个日期相减
int Date::operator-(const Date& d)
{Date max = *this;Date min = d;int flag = 1;if (*this < d){flag = -1;max = d;min = *this;}int n = 0;while (min != max){++min;++n;}return flag * n;
}
🫠这三个日期功能在日常中实际使用最频繁,因此需要保证函数功能的正确性。
第三部分: Test.cpp
#include "Date.h"void Test1()
{Date d1(2025, 8, 1);d1.Print();Date d2 = d1 + 100;d2.Print();//d1 += 10000;//d1.Print();
}void Test2()
{Date d3(2025, 8, 3);d3.Print();Date d4 = d3 - 215;d4.Print();//d3 -= 184;//d3.Print();
}void Test3()
{Date d1(2025, 8, 3);Date ret1 = d1++;ret1.Print();d1.Print();Date d2(2025, 8, 3);Date ret2 = ++d2;ret2.Print();d2.Print();
}void Test4()
{Date d1(2025, 8, 3);Date ret1 = d1--;ret1.Print();d1.Print();Date d2(2025, 8, 3);Date ret2 = --d2;ret2.Print();d2.Print();
}void Test5()
{Date d1(2025, 8, 3);Date d2(2025, 8, 4);cout << (d1 == d2) << endl;cout << (d1 != d2) << endl;
}int main()
{Test1();Test2();Test3();Test4();Test5();return 0;
}
😎Test.cpp 这部分内容是对前面函数功能的测试,大家可以自行修改。
第四部分:总结
🤗文章内有不熟悉的知识点,可以去看我写的关于运算符重载这篇文章👇:
C++基础知识点(五)- CSDN博客
💯如果这篇文章对你有用的话,请继续关注!
欢迎大家评论区留言交流,你的关注是我最大的动力 !