詳解如何用C++寫(xiě)一個(gè)日期計(jì)算器
前言
寫(xiě)一個(gè)日期計(jì)算器對(duì)學(xué)習(xí)的意義也很大。初學(xué)C++,接觸了類(lèi)和對(duì)象的概念,又認(rèn)識(shí)了默認(rèn)成員函數(shù),然后又學(xué)習(xí)了運(yùn)算符的重載。而日期計(jì)算器就很好的涵蓋了這些知識(shí)。能很好的幫助我們復(fù)習(xí)學(xué)過(guò)的知識(shí)。
代碼的布局
建兩個(gè) .cpp文件:Date.cpp Test.cpp
建一個(gè) .h文件 :Date.h
作用:Date.h聲明一個(gè)類(lèi) , Date.cpp類(lèi)的方法的具體實(shí)現(xiàn), Test.cpp測(cè)試方法的邏輯
設(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ù)相乘就沒(méi)有意義,也沒(méi)必要聲明。
方法的實(shí)現(xiàn)
獲取某年某月的天數(shù)
int GetMonthDay(int year, int month);
閏年
一年有365天,但地球公轉(zhuǎn)的周期比一年多了大約5.82個(gè)小時(shí)。所以每過(guò)4年,二月的28天就要變成29天,即4年一潤(rùn)。但每四年都要潤(rùn)一次的話(huà),每過(guò)100年,我們計(jì)算的天數(shù)要比地球公轉(zhuǎn)的天數(shù)多了大概0.75天,所以二月的28天保持不變,即百年不潤(rùn)。100年不潤(rùn)是為了補(bǔ)足4年一潤(rùn)的精度,而400一潤(rùn)是為了補(bǔ)足100年不潤(rùn)的精度。只有這樣,日期才不會(huì)與四季脫離。
總結(jié)就是:四年一潤(rùn),百年不潤(rùn),四百年又一潤(rùn)。
翻譯成計(jì)算機(jī)語(yǔ)言就是
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)闆](méi)有涉及到資源管理可以不寫(xiě),編譯器會(huì)自動(dòng)生成默認(rèn)的析構(gòu)函數(shù)。
日期+=天數(shù)
Date& operator+=(int day);
思路:可以把要加的天數(shù)直接加到日期的天數(shù)上,如果日期的天數(shù)沒(méi)有超過(guò)該月的最大天數(shù),直接返回日期。如果超過(guò)了,就寫(xiě)個(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ì)銷(xiāo)毀,可以引用返回 }
*this是返回聲明在頭文件中的日期類(lèi),該類(lèi)出了該函數(shù)的作用域不會(huì)銷(xiāo)毀,所以傳引用返回,提高效率。
日期+天數(shù)
Date operator+(int day);
實(shí)現(xiàn)日期+天數(shù)的時(shí)候不用把類(lèi)似于日期+=天數(shù)的邏輯再寫(xiě)一遍,可以直接復(fù)用。
Date Date::operator+(int day) { Date tmp = *this; //在實(shí)例化對(duì)象的時(shí)候,調(diào)用拷貝構(gòu)造函數(shù),將日期類(lèi)的數(shù)據(jù)拷貝給臨時(shí)對(duì)象 tmp += day; //直接復(fù)用+=的邏輯 return tmp; }
日期加天數(shù)不能改變?nèi)掌诘闹担砸獎(jiǎng)?chuàng)建臨時(shí)對(duì)象。臨時(shí)對(duì)象出了作用域就銷(xiāo)毀了,所以不能傳引用返回。
日期-天數(shù)
Date operator-(int day);
在實(shí)現(xiàn)日期+=天數(shù)和日期+天數(shù)的時(shí)候,先實(shí)現(xiàn)了+=的邏輯,在實(shí)現(xiàn)+的邏輯的時(shí)候復(fù)用+=的邏輯。現(xiàn)在反過(guò)來(lái),先實(shí)現(xiàn)-的邏輯,在實(shí)現(xiàn)-=的邏輯的時(shí)候復(fù)用-的邏輯。
思路:把天數(shù)直接和日期中的天數(shù)相減。若不為負(fù)數(shù),直接返回。若為負(fù)數(shù),則需要寫(xiě)個(gè)循環(huán)不斷向前借位,如果把月借成負(fù)數(shù)就向年借,然后調(diào)整月,再調(diào)整日,直到日大于零為止。因?yàn)槿掌?天數(shù)不改變?nèi)掌?,所以要?jiǎng)?chuàng)建臨時(shí)的對(duì)象。
代碼
Date Date::operator-(int day) { Date d = (*this); //創(chuàng)建臨時(shí)對(duì)象,把日期類(lèi)的數(shù)據(jù)拷貝給臨時(shí)對(duì)象 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í)對(duì)象出了作用域要銷(xiāo)毀,所以不能傳引用返回。
日期-=天數(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í)對(duì)象,返回值可以是引用 { ++_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; //返回該類(lèi) }
后置++
Date Date::operator++(int)
可直接復(fù)用前置++,后置++需要先使用再++,所以需要?jiǎng)?chuàng)建臨時(shí)對(duì)象,代碼如下
Date Date::operator++(int) { Date d = *this; ++(*this); return d; }
后置--
Date Date::operator--(int)
與++的實(shí)現(xiàn)不同,--的話(huà)先實(shí)現(xiàn)后置再實(shí)現(xiàn)前置。代碼如下
Date Date::operator--(int) { Date d = *this; // 創(chuàng)建臨時(shí)對(duì)象,保存日期類(lèi)中的值 --_day; //日期類(lèi)中的天數(shù)減一 if (_day <= 0) //這里可以不用寫(xiě)小于,因?yàn)橐惶煲惶斓臏p是不可能跨過(guò)零來(lái)到負(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)擊”日期-日期“即可跳過(guò)
>=運(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ù),返回值類(lèi)型為 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í)候把域作用限定符寫(xiě)在了返回值的前面,如下
大家不要這樣寫(xiě)呀。
在寫(xiě)前置++的時(shí)候?qū)懥艘粋€(gè)不易察覺(jué)的bug,寫(xiě)完測(cè)了幾組數(shù)據(jù)沒(méi)問(wèn)題,但其他方法調(diào)用的時(shí)候卻出問(wèn)題了,調(diào)了好久才發(fā)現(xiàn),如下代碼,大家能看出來(lái)哪里出錯(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)檫壿嬏樍耍雎粤艘恍┣闆r如下圖
大家在測(cè)方法的時(shí)候盡量要跨過(guò)幾個(gè)平年和閏年,這樣方法才有可信度。
以上就是詳解如何用C++寫(xiě)一個(gè)日期計(jì)算器的詳細(xì)內(nèi)容,更多關(guān)于C++日期計(jì)算器的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
C語(yǔ)言?huà)呃自敿?xì)代碼分步實(shí)現(xiàn)流程
掃雷是電腦上很經(jīng)典的游戲,特意去網(wǎng)上玩了一會(huì),幾次調(diào)試之后,發(fā)現(xiàn)這個(gè)比三子棋要復(fù)雜一些,尤其是空白展開(kāi)算法上和堵截玩家有的一拼,與實(shí)際游戲差別較大,不能使用光標(biāo),下面來(lái)詳解每一步分析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-01c++中為什么可以通過(guò)指針或引用實(shí)現(xiàn)多態(tài)詳解
這篇文章主要給大家介紹了關(guān)于c++中為何可以通過(guò)指針或引用實(shí)現(xiàn)多態(tài),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-04-04詳解C語(yǔ)言動(dòng)態(tài)內(nèi)存的分配
這篇文章主要為大家介紹了C語(yǔ)言動(dòng)態(tài)內(nèi)存的分配,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助2021-12-12C語(yǔ)言模擬實(shí)現(xiàn)字符串庫(kù)函數(shù)的示例講解
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言模擬實(shí)現(xiàn)字符串庫(kù)函數(shù)的具體方法,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-01-01C++實(shí)現(xiàn)LeetCode(105.由先序和中序遍歷建立二叉樹(shù))
這篇文章主要介紹了C++實(shí)現(xiàn)LeetCode(105.由先序和中序遍歷建立二叉樹(shù)),本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-07-07