<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
在我們的日常生活中,我們可能需要計算幾天後的日期,或計算日期差等,現如今計算日期的方式有很多,簡單粗暴的直接檢視日曆,快捷點的直接使用日期計算器來求得,先給一個網路上的日期計算器截圖:
現在,就讓我們用程式碼來實現其工作原理吧。
注意:本篇日期類.h檔案放宣告,.cpp檔案放定義
實現日期類首先就得檢查日期的合法性,這其中就包括大小月,閏年的2月有29天,一年只有12個月等等細節都要考慮到。
class Date { public: bool isLeapYear(int year) //判斷是否為閏年 { //四年一閏百年不閏或四百年一閏 return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0); } static int GetMonthDay(int year, int month) //加上static防止函數頻繁呼叫開闢幾十個位元組大小的陣列,最好加上 { int monthDayArray[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 }; if (month == 2 && isLeapYear(year)) return 29; //閏月29天 else return monthDayArray[month]; } Date(int year = 1, int month = 1, int day = 1) { if (year >= 1 && month <= 12 && month >= 1 && day <= GetMonthDay(year, month)) { //確保日期合法 _year = year; _month = month; _day = day; } } private: int _year; int _month; int _day; };
思路:
< 運運算元過載在我上一篇博文已經詳細講解過,主要是先把大於的情況全部統計出來,就比如我要比較範例化物件d1是否小於範例化物件d2,只需考慮如下三種滿足的情況:
這三種全是小於的情況,返回true,其餘返回false
程式碼如下:
// <運運算元過載 bool Date::operator<(const Date& d) const //類外存取成員函數需要設定類域 { if (_year < d._year || _year == d._year && _month < d._month || _year == d._year && _month == d._month && _day < d._day) return true; else return false; }
思路:
== 運運算元過載其實非常簡單,只需要判斷d1和d2的年、月、天是否對應相等即可:
程式碼如下:
// ==運運算元過載 bool Date::operator==(const Date& d) const { return _year == d._year && _month == d._month && _day == d._day; }
思路: -- 複用
<= 的運運算元過載,這裡要仔細想一想 <= 成立的條件是啥。不就是 要麼 < 要麼 = 嗎?我們只需要複用先前寫的 < 運運算元過載和 <=運運算元過載,無需自己費老大勁推導其內部原理。
程式碼如下:
// <=運運算元過載 bool Date::operator<=(const Date& d) const { return *this < d || *this == d; }
思路: -- 複用
> 的反義就是 <=,所以我們只需要複用 <= 運運算元過載,再對其取反即可解決此問題。
程式碼如下:
// >運運算元過載 bool Date::operator>(const Date& d) const { return !(*this <= d); }
思路: -- 複用
>= 的反義就是 <,所以我們只需要複用 < 運運算元過載,再對其取反即可。
程式碼如下:
// >=運運算元過載 bool Date::operator>=(const Date& d) const { return !(*this < d); }
思路: -- 複用
有了前面的基礎,寫個 != 也很簡單,對 == 取反即可
程式碼如下:
//!=運運算元過載 bool Date::operator!=(const Date& d) const { return !(*this == d); }
上述我們寫的運運算元過載都是建立在宣告定義分離的,這裡我們可以對其進行優化,如下:
先前我們學過內聯,可以幫助我們對於短小函數減少函數呼叫而引發的效率損失問題,因此我們可以把上述幾個運運算元過載函數放成內聯,此外,有一種簡單粗暴的方法:直接在類裡定義,因為類裡的函數預設內聯,還省的我們自己寫inline,而且我們也不用在類外加上類域了,當然,有些長的函數還是宣告和定義分離比較好。
Date.h 檔案:
#pragma once #include<iostream> #include<assert.h> using std::cout; using std::cin; using std::endl; class Date { public: bool isLeapYear(int year) { //四年一潤百年不潤或四百年一潤 return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0); } //獲取某月天數 int GetMonthDay(int year, int month); //建構函式 Date(int year = 1, int month = 1, int day = 1); //列印 void Print() const { cout << _year << "-" << _month << "-" << _day << endl; } // <運運算元過載 bool operator<(const Date& d) const; // ==運運算元過載 bool operator==(const Date& d) const; // <=運運算元過載 bool operator<=(const Date& d) const { return *this < d || *this == d; } // >運運算元過載 bool operator>(const Date& d) const { return !(*this <= d); //return (d < *this); } // >=運運算元過載 bool operator>=(const Date& d) const { return !(*this < d); } // !=運運算元過載 bool operator!=(const Date& d) const { return !(*this == d); } private: int _year; int _month; int _day; };
Date.cpp 檔案:
#include"Date.h" //獲取某月天數 int Date::GetMonthDay(int year, int month) { assert(year >= 0 && month > 0 && month < 13); static int monthDayArray[13] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 }; if (month == 2 && isLeapYear(year)) return 29; else return monthDayArray[month]; } //建構函式 Date::Date(int year, int month, int day) { if (year >= 1 && month <= 12 && month >= 1 && day <= GetMonthDay(year, month)) { _year = year; _month = month; _day = day; } else cout << "日期非法" << endl; } // <運運算元過載 bool Date::operator<(const Date& d) const //類外存取成員函數需要設定類域 { if (_year < d._year || _year == d._year && _month < d._month || _year == d._year && _month == d._month && _day < d._day) return true; else return false; } // ==運運算元過載 bool Date::operator==(const Date& d) const { return _year == d._year && _month == d._month && _day == d._day; }
思路:
對於日期 + 天數,我們得到的還是一個日期。特別需要注意進位的問題(天滿了往月進,月滿了往年進),主要考慮如下幾個特殊點:
加過的天數超過該月的最大天數,需要進位
當月進位到13時,年進位+1,月置為1
法一:
Date Date::operator+(int day) const { Date ret(*this); //拷貝構造,拿d1去初始化ret ret._day += day; while (ret._day > GetMonthDay(ret._year, ret._month)) { ret._day -= GetMonthDay(ret._year, ret._month); ret._month++; if (ret._month == 13) { ret._year++; ret._month = 1; } } return ret; }
出了作用域,物件ret不在,它是一個區域性物件,我們這裡不能用參照,用了的話,返回的就是ret的別名,但是ret又已經銷燬了,存取野指標了,所以出了作用域,如果物件不在了,就不能用參照返回,要用傳值返回
法二:複用日期+=天數
此法是建立在日期+=天數的基礎上完成的,這裡各位可以先看下文日期+=天數,然後我們進行復用:
Date Date::operator+(int day) const { //法二:複用日期 += 天數 Date ret(*this); ret += day; return ret; }
法一和法二熟優?
答案:法二更好,也就是用+去複用+=,具體原因在下文會解釋。
這裡實現 += 其實有兩種方案
法一:
前面我實現的日期+天數,仔細觀察我的程式碼,函數的第一行,我就呼叫了一個拷貝構造:
Date ret(*this); //拷貝構造,拿d1去初始化ret
這裡呼叫拷貝構造,是為了不在*this本身上做變動,只在ret上進行操作,其理由是日期+天數得到的是另一個日期,而不用拷貝構造直接在*this上做改動只會導致原有的日期也變化,而這個變化正是我日期 += 天數的需求
仔細想想:+=天數就是在原有的日期上再加一定的天數,直接對*this做手腳即可,因此只需對日期+天數的程式碼進行小改動即可:
Date& Date::operator+=(int day) //傳參照返回 { //如果day小於0,要單獨處理 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; }
注意這裡是傳參照返回,原因就在於我返回的*this是全域性的,出了作用域還在
法二:複用日期 +天數
Date& Date::operator+=(int day) { //法二:複用 * this = *this + day; //讓d1+過天數後再返回給自己從而實現+= return *this; }
法一和法二熟優?
答案:法一。其實討論這個問題就是在討論用+去複用+=號還是用+=複用+號,答案是用+去複用+=好,因為+有兩次拷貝,而+=沒有拷貝,所以實現+=,並且用+去複用+=效率更高
思路:
日期-=天數得到的還是一個日期,且是在原日期的基礎上做改動。合法的日期減去天數後的day只要>0就沒問題,若小於0就要借位了。要注意當減去的天數<0時單獨討論。具體步驟如下:
程式碼如下:
//日期 -=天數 d1-=100 Date& Date::operator-=(int day) { //如果減去的天數是負數,要單獨處理,直接呼叫+=運運算元過載 if (day < 0) { return *this += -day; } _day -= day; while (_day <= 0) { --_month; if (_month == 0) { _month = 12; --_year; } _day += GetMonthDay(_year, _month); } return *this; }
有了先前日期+和+=的基礎,這裡實現日期 - 天數直接複用日期 -= 天數即可:
//日期 - 天數 Date Date::operator-(int day) const { Date ret(*this); ret -= day; return ret; }
思路:
C++裡有前置++和後置++,這就導致一個巨大的問題,該如何區分它們,具體實現過程不難(直接複用+=即可),難的是如何區分前置和後置。因此C++規定,無參的為前置,有參的為後置。
程式碼如下:
//前置++ Date& Date::operator++() //無參的為前置 { *this += 1; //直接複用+= return *this; }
思路:
有參的即為後置,後置++拿到的返回值應該是自己本身未加過的,因此要先把自己儲存起來,再++*this,隨後返回自己。
程式碼如下:
//後置++ Date Date::operator++(int i) //有引數的為後置 { Date tmp(*this); *this += 1; //複用+= return tmp; }
思路:
前置--和前置++沒啥區別,只不過內部複用的是-=
程式碼如下:
//前置-- Date& Date::operator--() //無參的為前置 { *this -= 1; //直接複用-= return *this; }
思路:
後置--和後置++類似,只不過內部複用的是-=,不再贅述
程式碼如下:
//後置-- Date Date::operator--(int i) //有引數的為後置 { Date tmp(*this); *this -= 1; return tmp; }
思路:
日期 - 日期得到的是天數,首先我們得判斷兩個日期的大小,用min和max代替小的和大的,隨後,算出min和max之間的差距,若min!=max,則min就++,隨即定義變數n也自增++,最後返回n(注意符號)
程式碼如下:
//日期 - 日期 int Date::operator-(const Date& d) const { int flag = 1; //方便後續計算正負 Date max = *this; Date min = d; if (*this < d) { min = *this; max = d; flag = -1; //計算正負 } //確保max是大的,min是小的 int n = 0; while (min != max) { min++; n++; }//算出min和max之間絕對值差距 return n * flag; //如果d1大,結果為正,d2大結果為負 }
本篇日期類把先前學到的參照,傳值/傳參照返回、拷貝構造、複用等等知識點柔和到了一起,非常值得大家操手練習練習,創作不易,還望三連。
日期類的原始碼連結:gitee連結:日期計算器完善版
到此這篇關於基於C++實現日期計算器的文章就介紹到這了,更多相關C++日期計算器內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45