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

設計數(shù)據(jù)
年:_year 月:_month 日:_day
變量名前面加“ _ ”符號,是為了和普通的數(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); // 賦值運算符重載 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--(); // >運算符重載 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); // 日期-日期 返回天數(shù) int operator-(const Date& d);
聲明的方法要有其意義,比如日期和天數(shù)相乘就沒有意義,也沒必要聲明。
方法的實現(xiàn)
獲取某年某月的天數(shù)
int GetMonthDay(int year, int month);
閏年
一年有365天,但地球公轉(zhuǎn)的周期比一年多了大約5.82個小時。所以每過4年,二月的28天就要變成29天,即4年一潤。但每四年都要潤一次的話,每過100年,我們計算的天數(shù)要比地球公轉(zhuǎn)的天數(shù)多了大概0.75天,所以二月的28天保持不變,即百年不潤。100年不潤是為了補足4年一潤的精度,而400一潤是為了補足100年不潤的精度。只有這樣,日期才不會與四季脫離。
總結(jié)就是:四年一潤,百年不潤,四百年又一潤。
翻譯成計算機語言就是
year % 4 == 0 && year % 100 != 0 || year % 400 == 0
有了閏年的概念,那么獲取某年某月的天數(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];
}
}下圖是代碼控制的細節(jié)

下面加*的函數(shù)不做重點
*全缺省的構(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ù)在定義的時候不需要給缺省值。
拷貝構(gòu)造函數(shù)
Date(const Date& d);
Date::Date(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}賦值運算符重載
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ù)
因為沒有涉及到資源管理可以不寫,編譯器會自動生成默認的析構(gòu)函數(shù)。
日期+=天數(shù)
Date& operator+=(int day);
思路:可以把要加的天數(shù)直接加到日期的天數(shù)上,如果日期的天數(shù)沒有超過該月的最大天數(shù),直接返回日期。如果超過了,就寫個循環(huán)往前進位,直到日期的天數(shù)小于該月的最大天數(shù),然后再返回日期。
邏輯示意圖

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

因為臨時對象出了作用域要銷毀,所以不能傳引用返回。
日期-=天數(shù)
Date& operator-=(int day);
直接復用-的邏輯,代碼如下
Date& Date::operator-=(int day)
{
return *this = *this - day;
}前置++
Date& Date::operator++()
實現(xiàn)前置++就不需要復雜的邏輯了,只需要控制年月日的進位即可。代碼如下
Date& Date::operator++() //前置++需要先++在使用,所以不需要創(chuàng)建臨時對象,返回值可以是引用
{
++_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)
可直接復用前置++,后置++需要先使用再++,所以需要創(chuàng)建臨時對象,代碼如下
Date Date::operator++(int)
{
Date d = *this;
++(*this);
return d;
}后置--
Date Date::operator--(int)
與++的實現(xiàn)不同,--的話先實現(xiàn)后置再實現(xiàn)前置。代碼如下
Date Date::operator--(int)
{
Date d = *this; // 創(chuàng)建臨時對象,保存日期類中的值
--_day; //日期類中的天數(shù)減一
if (_day <= 0) //這里可以不用寫小于,因為一天一天的減是不可能跨過零來到負數(shù)的
{
--_month; //如果天數(shù)等于零了,就需要借上個月的天數(shù),月要減一
if (_month <= 0) //月不合法就調(diào)整月
{
--_year;
_month = 12;
}
_day = GetMonthDay(_year, _month); //把天數(shù)置成該月最大天數(shù)
}
return d; //返回保存好的數(shù)據(jù),這樣就實現(xiàn)了后置--的效果
}前置--
Date& Date::operator--()
直接復用后置--,代碼如下
Date& Date::operator--()
{
(*this)--;
return *this;
}實現(xiàn)比較大小運算符重載思路
小編先理一下思路,方便大家理解。
要實現(xiàn)比較大小的運算符有 >, ==, >=, < , <= , !=。只需要實現(xiàn)> 和 ==就可以復用并實現(xià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; //不然就是小的
}==運算符重載
bool Date::operator==(const Date& d)
年月日都相等才相等,代碼如下
bool Date::operator==(const Date& d)
{
return _year == d._year
&& _month == d._month
&& _day == d._day;
}*> = 運算符復用實現(xiàn)其他比較運算符重載
這里不做重點,大家可以點擊“目錄”,再點擊”日期-日期“即可跳過
>=運算符重載
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 !((*this) == d);
}日期-日期
int Date::operator-(const Date& d)
參數(shù):第一個參數(shù)為隱含的this指針,第二個參數(shù)為 const Date&,傳引用是為了提高傳值效率。
返回值:日期-日期代表的是兩個日期之間相差的天數(shù),返回值類型為 int。
思路1:可以先算出兩個日期相差多少年,把每一年的總天數(shù)加在一起,但要判斷該年是否為閏年。
思路2:直接復用++運算符,在設一個變量,每加一天,變量就加一。
下面用思路2實現(xiàn),代碼如下
int Date::operator-(const Date& d)
{
int counst = 0; //定義一個變量,保存天數(shù)
if ((*this) == d) //如果兩個日期相等,直接返回零
{
return 0;
}
else if ((*this) > d)
{
Date tmp = d; //如果this的的日期大,就給d創(chuàng)建臨時變量tmp,然tmp小日期去追this大日期
while ((*this) != tmp)
{
++tmp;
counst++;
}
return counst;
}
else
{
Date tmp = (*this); //同上
while (tmp != d)
{
++tmp;
counst++;
}
return counst;
}
}代碼錯誤和bug分享
小編在實現(xiàn)方法的時候把域作用限定符寫在了返回值的前面,如下


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

大家在測方法的時候盡量要跨過幾個平年和閏年,這樣方法才有可信度。
以上就是詳解如何用C++寫一個日期計算器的詳細內(nèi)容,更多關于C++日期計算器的資料請關注腳本之家其它相關文章!
相關文章
基于matlab實現(xiàn)DCT數(shù)字水印嵌入與提取
數(shù)字水印技術是將一些標識信息直接嵌入數(shù)字載體當中,?或間接表示在信號載體中,?且不影響原載體的使用價值。本文主要為大家介紹了基于matlab如何實現(xiàn)數(shù)字水印的嵌入與提取,感興趣的可以學習一下2022-01-01
c++中為什么可以通過指針或引用實現(xiàn)多態(tài)詳解
這篇文章主要給大家介紹了關于c++中為何可以通過指針或引用實現(xiàn)多態(tài),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2021-04-04
C++實現(xiàn)LeetCode(105.由先序和中序遍歷建立二叉樹)
這篇文章主要介紹了C++實現(xiàn)LeetCode(105.由先序和中序遍歷建立二叉樹),本篇文章通過簡要的案例,講解了該項技術的了解與使用,以下就是詳細內(nèi)容,需要的朋友可以參考下2021-07-07

