詳解如何用C++寫一個(gè)日期計(jì)算器
前言
寫一個(gè)日期計(jì)算器對學(xué)習(xí)的意義也很大。初學(xué)C++,接觸了類和對象的概念,又認(rèn)識(shí)了默認(rèn)成員函數(shù),然后又學(xué)習(xí)了運(yùn)算符的重載。而日期計(jì)算器就很好的涵蓋了這些知識(shí)。能很好的幫助我們復(fù)習(xí)學(xué)過的知識(shí)。
代碼的布局
建兩個(gè) .cpp文件:Date.cpp Test.cpp
建一個(gè) .h文件 :Date.h
作用:Date.h聲明一個(gè)類 , Date.cpp類的方法的具體實(shí)現(xiàn), Test.cpp測試方法的邏輯

設(shè)計(jì)數(shù)據(jù)
年:_year 月:_month 日:_day
變量名前面加“ _ ”符號(hào),是為了和普通的數(shù)據(jù)或一些參數(shù)做區(qū)分。增加代碼可讀性。
方法聲明
// 獲取某年某月的天數(shù) int GetMonthDay(int year, int month); // 全缺省的構(gòu)造函數(shù) Date(int year = 2024, int month = 4, int day = 18); // 拷貝構(gòu)造函數(shù) Date(const Date& d); // 賦值運(yùn)算符重載 Date& operator=(const Date& d); // 析構(gòu)函數(shù) ~Date(); // 日期+=天數(shù) Date& operator+=(int day); //其結(jié)果為日期 // 日期+天數(shù) Date operator+(int day); // 日期-天數(shù) Date operator-(int day); // 日期-=天數(shù) Date& operator-=(int day); // 前置++ Date& operator++(); //天數(shù)加1 // 后置++ Date operator++(int); // 后置-- Date operator--(int); //天數(shù)減1 // 前置-- Date& operator--(); // >運(yùn)算符重載 bool operator>(const Date& d); //比較日期大小 // ==運(yùn)算符重載 bool operator==(const Date& d); // >=運(yùn)算符重載 bool operator >= (const Date& d); // <運(yùn)算符重載 bool operator < (const Date& d); // <=運(yùn)算符重載 bool operator <= (const Date& d); // !=運(yùn)算符重載 bool operator != (const Date& d); // 日期-日期 返回天數(shù) int operator-(const Date& d);
聲明的方法要有其意義,比如日期和天數(shù)相乘就沒有意義,也沒必要聲明。
方法的實(shí)現(xiàn)
獲取某年某月的天數(shù)
int GetMonthDay(int year, int month);
閏年
一年有365天,但地球公轉(zhuǎn)的周期比一年多了大約5.82個(gè)小時(shí)。所以每過4年,二月的28天就要變成29天,即4年一潤。但每四年都要潤一次的話,每過100年,我們計(jì)算的天數(shù)要比地球公轉(zhuǎn)的天數(shù)多了大概0.75天,所以二月的28天保持不變,即百年不潤。100年不潤是為了補(bǔ)足4年一潤的精度,而400一潤是為了補(bǔ)足100年不潤的精度。只有這樣,日期才不會(huì)與四季脫離。
總結(jié)就是:四年一潤,百年不潤,四百年又一潤。
翻譯成計(jì)算機(jī)語言就是
year % 4 == 0 && year % 100 != 0 || year % 400 == 0
有了閏年的概念,那么獲取某年某月的天數(shù)的代碼就可以實(shí)現(xiàn)了
// 獲取某年某月的天數(shù)
int Date::GetMonthDay(int year, int month)
{
assert(month > 0 && month < 13);
static int a[13] = { -1,31, 28, 31, 30, 31, 30, 31, 31, 30, 31,30,31 };
if (2 == month && year % 4 == 0 && year % 100 != 0 || year % 400 == 0)
{
return a[month] + 1;
}
else
{
return a[month];
}
}下圖是代碼控制的細(xì)節(jié)

下面加*的函數(shù)不做重點(diǎn)
*全缺省的構(gòu)造函數(shù)
Date(int year = 2024, int month = 4, int day = 18);
Date::Date(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}注意:全缺省構(gòu)造函數(shù)在定義的時(shí)候不需要給缺省值。
拷貝構(gòu)造函數(shù)
Date(const Date& d);
Date::Date(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}賦值運(yùn)算符重載
Date& operator=(const Date& d);
Date& Date::operator=(const Date& d)
{
if (this != &d)
{
_year = d._year;
_month = d._month;
_day = d._day;
return *this;
}
return *this;
}析構(gòu)函數(shù)
因?yàn)闆]有涉及到資源管理可以不寫,編譯器會(huì)自動(dòng)生成默認(rèn)的析構(gòu)函數(shù)。
日期+=天數(shù)
Date& operator+=(int day);
思路:可以把要加的天數(shù)直接加到日期的天數(shù)上,如果日期的天數(shù)沒有超過該月的最大天數(shù),直接返回日期。如果超過了,就寫個(gè)循環(huán)往前進(jìn)位,直到日期的天數(shù)小于該月的最大天數(shù),然后再返回日期。
邏輯示意圖

代碼
Date& Date::operator+=(int day)
{
_day += day; //把天數(shù)加到日期的天數(shù)上
while (_day > GetMonthDay(_year, _month)) //如果日期的天數(shù)大于該月最大天數(shù)就進(jìn)位
{
_day -= GetMonthDay(_year, _month); //要想進(jìn)位,得把該月的最大天數(shù)減掉
++_month; //進(jìn)位
if (_month > 12) //如果月不合法就調(diào)整月
{
++_year;
_month = 1;
}
}
return *this; //返回日期,因?yàn)槌隽俗饔糜虿粫?huì)銷毀,可以引用返回
}*this是返回聲明在頭文件中的日期類,該類出了該函數(shù)的作用域不會(huì)銷毀,所以傳引用返回,提高效率。
日期+天數(shù)
Date operator+(int day);
實(shí)現(xiàn)日期+天數(shù)的時(shí)候不用把類似于日期+=天數(shù)的邏輯再寫一遍,可以直接復(fù)用。
Date Date::operator+(int day)
{
Date tmp = *this; //在實(shí)例化對象的時(shí)候,調(diào)用拷貝構(gòu)造函數(shù),將日期類的數(shù)據(jù)拷貝給臨時(shí)對象
tmp += day; //直接復(fù)用+=的邏輯
return tmp;
}日期加天數(shù)不能改變?nèi)掌诘闹担砸獎(jiǎng)?chuàng)建臨時(shí)對象。臨時(shí)對象出了作用域就銷毀了,所以不能傳引用返回。
日期-天數(shù)
Date operator-(int day);
在實(shí)現(xiàn)日期+=天數(shù)和日期+天數(shù)的時(shí)候,先實(shí)現(xiàn)了+=的邏輯,在實(shí)現(xiàn)+的邏輯的時(shí)候復(fù)用+=的邏輯。現(xiàn)在反過來,先實(shí)現(xiàn)-的邏輯,在實(shí)現(xiàn)-=的邏輯的時(shí)候復(fù)用-的邏輯。
思路:把天數(shù)直接和日期中的天數(shù)相減。若不為負(fù)數(shù),直接返回。若為負(fù)數(shù),則需要寫個(gè)循環(huán)不斷向前借位,如果把月借成負(fù)數(shù)就向年借,然后調(diào)整月,再調(diào)整日,直到日大于零為止。因?yàn)槿掌?天數(shù)不改變?nèi)掌?,所以要?jiǎng)?chuàng)建臨時(shí)的對象。
代碼
Date Date::operator-(int day)
{
Date d = (*this); //創(chuàng)建臨時(shí)對象,把日期類的數(shù)據(jù)拷貝給臨時(shí)對象
d._day -= day; //讓日期的天數(shù)直接和天數(shù)相減
while (d._day <= 0) //日期的天數(shù)小于零就調(diào)整
{
--d._month; //該月已經(jīng)是負(fù)的,應(yīng)該往下個(gè)月借天數(shù)
if (d._month <= 0) //月不合法就調(diào)整月
{
--d._year;
d._month = 12;
}
d._day += GetMonthDay(d._year, d._month); //把該月的所有天數(shù)都借給日期的天數(shù)
}
return d;
}邏輯示意圖

因?yàn)榕R時(shí)對象出了作用域要銷毀,所以不能傳引用返回。
日期-=天數(shù)
Date& operator-=(int day);
直接復(fù)用-的邏輯,代碼如下
Date& Date::operator-=(int day)
{
return *this = *this - day;
}前置++
Date& Date::operator++()
實(shí)現(xiàn)前置++就不需要復(fù)雜的邏輯了,只需要控制年月日的進(jìn)位即可。代碼如下
Date& Date::operator++() //前置++需要先++在使用,所以不需要?jiǎng)?chuàng)建臨時(shí)對象,返回值可以是引用
{
++_day; //天數(shù)加一
if (_day > GetMonthDay(_year, _month)) //如果天數(shù)不符合該月最大天數(shù),則需要調(diào)整
{
++_month; //讓月加一
if (_month > 12) //月不合法就調(diào)整月
{
++_year;
_month = 1;
}
_day = 1; 讓天數(shù)置一
}
return *this; //返回該類
}后置++
Date Date::operator++(int)
可直接復(fù)用前置++,后置++需要先使用再++,所以需要?jiǎng)?chuàng)建臨時(shí)對象,代碼如下
Date Date::operator++(int)
{
Date d = *this;
++(*this);
return d;
}后置--
Date Date::operator--(int)
與++的實(shí)現(xiàn)不同,--的話先實(shí)現(xiàn)后置再實(shí)現(xiàn)前置。代碼如下
Date Date::operator--(int)
{
Date d = *this; // 創(chuàng)建臨時(shí)對象,保存日期類中的值
--_day; //日期類中的天數(shù)減一
if (_day <= 0) //這里可以不用寫小于,因?yàn)橐惶煲惶斓臏p是不可能跨過零來到負(fù)數(shù)的
{
--_month; //如果天數(shù)等于零了,就需要借上個(gè)月的天數(shù),月要減一
if (_month <= 0) //月不合法就調(diào)整月
{
--_year;
_month = 12;
}
_day = GetMonthDay(_year, _month); //把天數(shù)置成該月最大天數(shù)
}
return d; //返回保存好的數(shù)據(jù),這樣就實(shí)現(xiàn)了后置--的效果
}前置--
Date& Date::operator--()
直接復(fù)用后置--,代碼如下
Date& Date::operator--()
{
(*this)--;
return *this;
}實(shí)現(xiàn)比較大小運(yùn)算符重載思路
小編先理一下思路,方便大家理解。
要實(shí)現(xiàn)比較大小的運(yùn)算符有 >, ==, >=, < , <= , !=。只需要實(shí)現(xiàn)> 和 ==就可以復(fù)用并實(shí)現(xiàn)后四個(gè)運(yùn)算符。如下圖

>運(yùn)算符重載
bool Date::operator>(const Date& d)
代碼如下
bool Date::operator>(const Date& d)
{
if (_year > d._year) //年大就大
{
return true;
}
if (_year == d._year)//年相等比月
{
if (_month > d._month) //月大就大
{
return true;
}
if (_month == d._month) //月相等比天
{
if (_day > d._day) //天大就大
{
return true;
}
}
}
return false; //不然就是小的
}==運(yùn)算符重載
bool Date::operator==(const Date& d)
年月日都相等才相等,代碼如下
bool Date::operator==(const Date& d)
{
return _year == d._year
&& _month == d._month
&& _day == d._day;
}*> = 運(yùn)算符復(fù)用實(shí)現(xiàn)其他比較運(yùn)算符重載
這里不做重點(diǎn),大家可以點(diǎn)擊“目錄”,再點(diǎn)擊”日期-日期“即可跳過
>=運(yùn)算符重載
bool Date::operator >= (const Date& d)
{
return (*this) > d || (*this) == d;
}<運(yùn)算符重載
bool Date::operator < (const Date & d)
{
return !((*this) >= d);
}<=運(yùn)算符重載
bool Date::operator <= (const Date& d)
{
return !((*this) > d);
}!=運(yùn)算符重載
bool Date::operator != (const Date& d)
{
return !((*this) == d);
}日期-日期
int Date::operator-(const Date& d)
參數(shù):第一個(gè)參數(shù)為隱含的this指針,第二個(gè)參數(shù)為 const Date&,傳引用是為了提高傳值效率。
返回值:日期-日期代表的是兩個(gè)日期之間相差的天數(shù),返回值類型為 int。
思路1:可以先算出兩個(gè)日期相差多少年,把每一年的總天數(shù)加在一起,但要判斷該年是否為閏年。
思路2:直接復(fù)用++運(yùn)算符,在設(shè)一個(gè)變量,每加一天,變量就加一。
下面用思路2實(shí)現(xiàn),代碼如下
int Date::operator-(const Date& d)
{
int counst = 0; //定義一個(gè)變量,保存天數(shù)
if ((*this) == d) //如果兩個(gè)日期相等,直接返回零
{
return 0;
}
else if ((*this) > d)
{
Date tmp = d; //如果this的的日期大,就給d創(chuàng)建臨時(shí)變量tmp,然tmp小日期去追this大日期
while ((*this) != tmp)
{
++tmp;
counst++;
}
return counst;
}
else
{
Date tmp = (*this); //同上
while (tmp != d)
{
++tmp;
counst++;
}
return counst;
}
}代碼錯(cuò)誤和bug分享
小編在實(shí)現(xiàn)方法的時(shí)候把域作用限定符寫在了返回值的前面,如下


大家不要這樣寫呀。
在寫前置++的時(shí)候?qū)懥艘粋€(gè)不易察覺的bug,寫完測了幾組數(shù)據(jù)沒問題,但其他方法調(diào)用的時(shí)候卻出問題了,調(diào)了好久才發(fā)現(xiàn),如下代碼,大家能看出來哪里出錯(cuò)了嗎
// 前置++
Date& Date::operator++() // bug分享
{
++_day;
if (_day > GetMonthDay(_year, _month))
{
++_month;
if (_month > 12)
{
++_year;
_month = 1;
_day = 1;
}
}
return *this;
}
哈哈,其實(shí)正是因?yàn)檫壿嬏樍?,忽略了一些情況如下圖

大家在測方法的時(shí)候盡量要跨過幾個(gè)平年和閏年,這樣方法才有可信度。
以上就是詳解如何用C++寫一個(gè)日期計(jì)算器的詳細(xì)內(nèi)容,更多關(guān)于C++日期計(jì)算器的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
C語言掃雷詳細(xì)代碼分步實(shí)現(xiàn)流程
掃雷是電腦上很經(jīng)典的游戲,特意去網(wǎng)上玩了一會(huì),幾次調(diào)試之后,發(fā)現(xiàn)這個(gè)比三子棋要復(fù)雜一些,尤其是空白展開算法上和堵截玩家有的一拼,與實(shí)際游戲差別較大,不能使用光標(biāo),下面來詳解每一步分析2022-02-02
基于matlab實(shí)現(xiàn)DCT數(shù)字水印嵌入與提取
數(shù)字水印技術(shù)是將一些標(biāo)識(shí)信息直接嵌入數(shù)字載體當(dāng)中,?或間接表示在信號(hào)載體中,?且不影響原載體的使用價(jià)值。本文主要為大家介紹了基于matlab如何實(shí)現(xiàn)數(shù)字水印的嵌入與提取,感興趣的可以學(xué)習(xí)一下2022-01-01
c++中為什么可以通過指針或引用實(shí)現(xiàn)多態(tài)詳解
這篇文章主要給大家介紹了關(guān)于c++中為何可以通過指針或引用實(shí)現(xiàn)多態(tài),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-04-04
C語言模擬實(shí)現(xiàn)字符串庫函數(shù)的示例講解
這篇文章主要為大家詳細(xì)介紹了C語言模擬實(shí)現(xiàn)字符串庫函數(shù)的具體方法,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-01-01
C++實(shí)現(xiàn)LeetCode(105.由先序和中序遍歷建立二叉樹)
這篇文章主要介紹了C++實(shí)現(xiàn)LeetCode(105.由先序和中序遍歷建立二叉樹),本篇文章通過簡要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-07-07

