欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C++日期類實(shí)現(xiàn)的完整操作

 更新時(shí)間:2024年06月26日 08:48:35   作者:很楠不愛  
C++標(biāo)準(zhǔn)庫沒有提供所謂的日期類型,C++繼承了C語言用于日期和時(shí)間操作的結(jié)構(gòu)和函數(shù),這篇文章主要給大家介紹了關(guān)于C++日期類實(shí)現(xiàn)的完整操作,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下

一.基本框架

根據(jù)我們過去實(shí)現(xiàn)項(xiàng)目的方法,我們需要將聲明與定義分離,同時(shí)還要實(shí)現(xiàn)測(cè)試代碼與源代碼分離,所以我們需要三個(gè)文件:

隨后進(jìn)行類的創(chuàng)建,基本成員函數(shù)的實(shí)現(xiàn),以及測(cè)試代碼的創(chuàng)建等框架。 

隨后進(jìn)行框架的測(cè)試:

二.日期的比較

兩個(gè)日期之間的比較方式有很多種:>、<、<=、>=、==、!=

這些就需要我們的賦值運(yùn)算符構(gòu)造函數(shù)出馬了。

上篇文章我們已經(jīng)知道的“>”的寫法:

bool 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)
			return _day > d._day;
		else
	    	return false;
	}
	else
		return false;
}

但是就這一個(gè)的寫法,就已經(jīng)是很多,很麻煩的一段代碼了,難道像這樣的代碼我們一共要寫6個(gè)嗎???當(dāng)然不需要,我們要知道,這些符號(hào)之間都有兩兩互補(bǔ)的關(guān)系。

比如說,我們現(xiàn)在寫出了“>”,那么“<=”不就是“>”取反嗎

bool Date::operator<=(const Date& d)
{
	return !(*this > d);
}

 我們建議把“==”給寫出來,因?yàn)樗容^容易寫:

bool Date::operator==(const Date& d)
{
	return _year == d._year
		&& _month == d._month
		&& _day == d._day;
}

如此一來,“!=”的寫法就會(huì)是:

bool Date::operator!=(const Date& d)
{
	return !(*this == d);
}

現(xiàn)在我們同時(shí)擁有了“>”和“==”,那么將兩者結(jié)合自然就得到了“>=”

bool Date::operator>=(const Date& d)
{
	return *this > d || *this == d;
}

這樣是不是非常的簡潔???其余符號(hào)的代碼就不一一列舉啦,詳情請(qǐng)看最后的完整代碼展示。

三.日期的加減運(yùn)算

日期的加減是一個(gè)相對(duì)比較困難的運(yùn)算,它不像數(shù)字的加減那樣簡單,因?yàn)?strong>不僅存在大小月的天數(shù)不一,而且每四年還會(huì)出現(xiàn)閏年的特殊情況,這樣就會(huì)導(dǎo)致進(jìn)位非常的麻煩。下面我們就來詳細(xì)分享一下,如此復(fù)雜的日期運(yùn)算,到底該怎么實(shí)現(xiàn)。 

1.得到月的天數(shù)

首先很重要的一點(diǎn)就是我們要能夠知道每個(gè)月都分別有多少天,同時(shí)還有2月這個(gè)特殊的月份,我們通過一個(gè)函數(shù)來實(shí)現(xiàn):

int GetMonthdays(int year, int month)
{
	assert(month > 0 && month < 13);
	int Monthdays[12] = { 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 Monthdays[month - 1];
}

首先要做的就是assert斷言,防止月份輸入錯(cuò)誤,其次因?yàn)殚c年是斯四年一次,所以我們默認(rèn)情況下都是平年,通過數(shù)組來記錄,能夠方便獲取。最后就是進(jìn)行閏年二月的判斷,如果2月,我們就去判斷一下是否是閏年。 

2.日期的加運(yùn)算

我們之間搬出代碼來講:

Date& Date::operator+(int day)
{
	_day += day;
	while(_day > GetMonthdays(_year, _month))
	{
		_day -= GetMonthdays(_year, _month);
		_month++;
		if (_month == 13)
		{
			_year++;
			_month = 1;
		}
	}
	return *this;
}

依舊是使用賦值運(yùn)算符構(gòu)造函數(shù),我們直接讓_day加上我們要加的天數(shù),隨后進(jìn)行判斷,如果相加之后的天數(shù)大于當(dāng)月的天數(shù),就讓_day減去該月的天數(shù),剩下的自然就是下個(gè)月的天數(shù),同時(shí)月份+1,如果月份+1后是13,那就需要向年進(jìn)一,同時(shí)月份回到1。

之所以使用循環(huán),是因?yàn)?strong>如果我要加100天,那向月的進(jìn)位就不止1了,所以要循環(huán)往復(fù)的判斷。

下面我們進(jìn)行測(cè)試:

#include"Date.h"
int main()
{
	Date d1(2024, 2, 1);
	Date d2 = d1 + 30;
	d2.Print();
	return 0;
}

結(jié)果如下: 

1+30 = 31,而2024年恰巧就是閏年,所以2月有29天31 - 29 = 2,所以結(jié)果為2024/3/2。 

但是這樣的寫法看似完美,但實(shí)際上存在一個(gè)很大的錯(cuò)誤,來看代碼:

#include"Date.h"
int main()
{
	Date d1(2024, 2, 1);
	Date d2 = d1 + 30;
	d2.Print();
	d1.Print();
	return 0;
}

 我們是讓d2對(duì)象去接收d1對(duì)象的日期加上20天后的日期,但實(shí)際上:

d1對(duì)象的日期也發(fā)生了改變

這個(gè)錯(cuò)誤其實(shí)也是可以理解的,因?yàn)槲覀?strong>在函數(shù)中直接默認(rèn)進(jìn)行操作的就是d1的成員變量。而這樣的運(yùn)算,實(shí)際上是“+=”運(yùn)算。

所以想要保證d1的成員變量不變,就必須創(chuàng)建一個(gè)臨時(shí)變量來代替

Date Date::operator+(int day)
{
	Date tmp(*this);
	tmp._day += day;
	while (tmp._day > GetMonthdays(tmp._year, tmp._month))
	{
		tmp._day -= GetMonthdays(tmp._year, tmp._month);
		tmp._month++;
		if (tmp._month == 13)
		{
			tmp._year++;
			tmp._month = 1;
		}
	}
	return tmp;
}

 創(chuàng)建臨時(shí)變量,就用到了我們的拷貝構(gòu)造函數(shù),使用tmp臨時(shí)變量代替d1對(duì)象進(jìn)行操作。

值得注意的一點(diǎn)是,由于tmp是臨時(shí)的變量,當(dāng)這個(gè)函數(shù)結(jié)束時(shí)就不存在了,所以其作為返回值時(shí),返回類型不能是引用。 

再進(jìn)行測(cè)試,結(jié)果如下:

3.日期的減運(yùn)算

理解了加運(yùn)算之后,減運(yùn)算的寫法相信小伙伴們都能夠自己悟出來了。

唯一值得注意的是,日期沒有0天

//日期減等運(yùn)算
Date& Date::operator-=(int day)
{
	_day -= day;
	while (_day <= 0)
	{
		_month--;
		if (_month == 0)
		{
			_year--;
			_month = 12;
		}
		_day += GetMonthdays(_year, _month);
	}
	return *this;
}

這里我們先來實(shí)現(xiàn)一下“-=”運(yùn)算,注意while循環(huán)的判斷條件,因?yàn)開day不可能等于0。

如果當(dāng)月剩余的天數(shù)不夠用,就需要去借用上個(gè)月的天數(shù)繼續(xù)減。結(jié)果如下:

那么問題來了,博主為什么要先實(shí)現(xiàn)“-=”呢 ???

下面我們就來看看“-”運(yùn)算的實(shí)現(xiàn):

//日期減運(yùn)算
Date Date::operator-(int day)
{
	Date tmp(*this);
	tmp -= day;
	return tmp;
}

怎么樣,有沒有很震驚,為了不改變d1對(duì)象,我們確實(shí)創(chuàng)建了臨時(shí)變量tmp,但是我們大可不必去再寫像上邊那樣的一大長串代碼,因?yàn)槲覀円呀?jīng)有“-=”運(yùn)算了,所以我們直接讓tmp去進(jìn)行“-=”運(yùn)算,就可以得到結(jié)果: 

而我們前邊實(shí)現(xiàn)過的加運(yùn)算同樣可以借用“+=”運(yùn)算來寫

//日期加運(yùn)算
Date Date::operator+(int day)
{
	Date tmp(*this);
	tmp += day;
	return tmp;
}

4.日期的++--運(yùn)算

我們知道,“++”和“--”運(yùn)算都有前置和后置兩種方式,那么我們?cè)撛趺从脴?gòu)造函數(shù)去分別實(shí)現(xiàn)呢?

不管是前置還是后置,它們都會(huì)有++,那么我們使用賦值運(yùn)算符重載函數(shù),函數(shù)名該怎么寫?難道也是一前一后???

并不是,實(shí)際上是使用函數(shù)重載來區(qū)分它們

	//前置++運(yùn)算
	Date& operator++();
	//后置++運(yùn)算
	Date operator++(int);

對(duì)于后置++,給它一個(gè)int參數(shù),但是該參數(shù)并不會(huì)使用,只是用作編譯器的區(qū)分。

那么兩個(gè)函數(shù)又該怎么實(shí)現(xiàn)呢??? 

要注意的是,前置++是先加1,再給值,而后置++是先給值,再++,所以后者就需要一個(gè)臨時(shí)變量,我們同樣借用一下“+=”函數(shù)

//前置++運(yùn)算
Date& Date::operator++()
{
	*this += 1;
	return *this;
}
//后置++運(yùn)算
Date Date::operator++(int)
{
	Date tmp = *this;
	*this += 1;
	return tmp;
}

再來進(jìn)行測(cè)試: 

如此便可以實(shí)現(xiàn)“++”的運(yùn)算符重載。“--”與之類似,博主這里就不做展開講解。

5.日期減日期

上邊我們講的日期減運(yùn)算,是用日期去減去明確的天數(shù)得到一個(gè)新的日期。

那么現(xiàn)在如果想用一個(gè)日期減去另一個(gè)日期,計(jì)算兩個(gè)日期之間有多少天,又該怎么搞呢???

這個(gè)事情看似復(fù)雜,實(shí)則代碼寫起來也挺簡單,現(xiàn)在給大家一個(gè)思想:

先去比較兩個(gè)日期誰大,然后我讓小的一直去++,并計(jì)數(shù),直到跟大的相等,計(jì)數(shù)的結(jié)果不就是兩者的相差天數(shù)嗎???

//日期-日期
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 n * flag;
}

先默認(rèn)前一個(gè)值為較大值,后一個(gè)為較小值,然后去比較,如果前一個(gè)實(shí)際上是較小值,則進(jìn)行互換,同時(shí)創(chuàng)建一個(gè)flag,如果是大-小,結(jié)果即為整數(shù),反之則賦值為-1,結(jié)果為負(fù)數(shù),測(cè)試如下:

6.日期的輸入輸出

我們前邊講述的日期,都是我們在創(chuàng)建對(duì)象時(shí)就給定的數(shù)據(jù),輸出時(shí)也是用的Print函數(shù)。而且我們知道,cin和cout是無法直接輸入輸出自定義類型的數(shù)據(jù)的。

那現(xiàn)在我們就想先創(chuàng)建一個(gè)對(duì)象,然后通過cin和cout來輸入輸出數(shù)據(jù),該如何實(shí)現(xiàn)呢???

首先我們要知道,cin是istream類型的對(duì)象,而cout是ostream類型的對(duì)象,那么我們就可以通過賦值運(yùn)算符重載函數(shù)來重載“>>”和“<<”兩個(gè)符號(hào)來實(shí)現(xiàn)

//日期輸出
void Date::operator<<(ostream& out)
{
	out << _year << "年" << _month << "月" << _day << "日" << endl;
}

但是這樣的寫法存在問題

賦值運(yùn)算符重載函數(shù)定義在類中作為成員函數(shù)時(shí),其第一個(gè)參數(shù)就會(huì)是默認(rèn)的隱藏的this參數(shù),也就是d1,而cout則是第二個(gè)參數(shù),這就導(dǎo)致我們調(diào)用函數(shù)時(shí)兩個(gè)實(shí)參的順序存在問題,如果將其改為d1<<cout,就能通過編譯:

但是這顯然不符合我們C++的使用規(guī)范,所以想要恢復(fù)順序,就需要將此函數(shù)定義在類外,交換兩個(gè)形參的位置

但是這個(gè)時(shí)候又出現(xiàn)了問題,因?yàn)?strong>該函數(shù)在類外,而類的成員變量是私有的,我們不能使用:

又該如何解決這個(gè)問題呢?

這就需要用到關(guān)鍵字:friend,通過friend,將類外函數(shù)在類內(nèi)進(jìn)行友元聲明,就可以啦:

但是此時(shí)還有一個(gè)問題,在C++中cout是支持同時(shí)輸出多個(gè)變量的,但是我們定義的函數(shù)卻不行:

這是因?yàn)榘凑?strong>從左到右的順序,執(zhí)行完cout<<d1之后,它們需要返回一個(gè)ostream類型的返回值來繼續(xù)和d2一起作為參數(shù)去繼續(xù)調(diào)用函數(shù),所以需要將該函數(shù)的返回值類型替換為ostream并返回out

//日期輸出
ostream& operator<<(ostream& out, const Date& d)
{
	out << d._year << "年" << d._month << "月" << d._day << "日" << endl;
	return out;
}

 測(cè)試如下:

那么知道輸出之后,輸入的寫法就與之類似了:

//日期輸入
istream& operator>>(istream& in, Date& d)
{
	in >> d._year >> d._month >> d._day;
	return in;
}

首先就是返回值類型和參數(shù)類型為istream&,其次要注意參數(shù)d不能用const修飾,因?yàn)榫褪且o它輸入值。

測(cè)試如下:

7.存在的問題

 到這里呢,日期類的所有基本功能已經(jīng)全部實(shí)現(xiàn)了,但是任然存在一個(gè)問題:

我們不小心將2月的天數(shù)傳了個(gè)40,這怎么能允許呢,2月最多也就29天,40天怎么可能呢?但是發(fā)現(xiàn)d2還是按部就班的進(jìn)行了“+”運(yùn)算,這就會(huì)出現(xiàn)很大的問題。所以我們需要進(jìn)行傳入檢查。

因?yàn)?strong>在構(gòu)造函數(shù)和輸入函數(shù)中都需要進(jìn)行檢查,所以我們需要一個(gè)創(chuàng)建一個(gè)函數(shù)

//檢查日期合法性
bool Date::CheckInvalid()
{
	if (_year <= 0 || _month < 1 || _month > 12
		|| _day < 1 || _day > GetMonthdays(_year, _month))
		return false;
	else
		return true;
}

 分別判斷年,月,日是否都合法。

//初始化
Date::Date(int year, int month, int day)
{
	_year = year;
	_month = month;
	_day = day;
	if (!CheckInvalid())
	{
		cout << *this << "該日期非法" << endl;
	    exit(-1);
	}
}

構(gòu)造函數(shù)中使用,若非法直接結(jié)束程序

//日期輸入
istream& operator>>(istream& in, Date& d)
{
	while(1)
	{
		in >> d._year >> d._month >> d._day;
		if (!d.CheckInvalid())
			cout << "輸入的日期非法,請(qǐng)重新輸入:" << endl;
		else
			break;
	}
	return in;
}

輸入函數(shù)中使用,若非法則重新輸入: 

總結(jié)

日期類的實(shí)現(xiàn)到這里就分享完啦,希望能夠幫助小伙伴們更加深入的理解類的內(nèi)部結(jié)構(gòu)及其成員函數(shù)的操作實(shí)現(xiàn)。

到此這篇關(guān)于C++日期類實(shí)現(xiàn)的文章就介紹到這了,更多相關(guān)C++日期類內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論