【原創】C++_第一週_Date類的實現


 

喜歡的朋友可以關注收藏一下

 

本文實現了一個date類型,原版要求如下(括號爲超出要求附加):

爲Date類實現如下成員:

1. 構造器,可以初始化年、月、日。(使用了模板)

2. 大於、小於、等於(> 、< 、==)操作符重載,進行日期比較。(>=,<=,==)

3. print() 打印出類似 2015-10-1 這樣的格式。(print(變長參數),printA(字符指針,date))

然後創建兩個全局函數:

1. 第1個函數 CreatePoints生成10個隨機的Date,並以數組形式返回;(自定義了隨機生成器類class Rand_int)

2. 第2個函數 Sort 對第1個函數CreatePoints生成的結果,將其按照從小到大進行排序。 (快速排序算法QuickSort)

最後在main函數中調用CreatePoints,並調用print將結果打印出來。然後調用Sort函數對前面結果處理後,並再次調用print將結果打印出來。

 

  首先我的整體規劃是class類的聲明及對他的操作函數都寫在頭文件裏,cpp裏只放main()函數。而不是通常我們常見的聲明放在頭文件,cpp裏放定義和main()函數。這麼做的理由是我把整個class date類當做一個抽象封裝,裏面有對它的定義、操作、和依賴,我的程序裏因爲時間關係並沒有寫進位的依賴關係,但留下了擴展的餘地,待以後擴展後再給大家分享後續操作。考慮到頭文件中包含的頭文件,和cpp中包含的頭文件衝突,我採用了以下保護措施。

 

#ifndef __DATE_H__
#define __DATE_H__



#endif

 

 

 

 

 

 

  那一個date類需要year,month,day.這三個變量爲了避免耦合我把它封裝在private裏面,只有通過成員函數 year_fun()  month_fun()  day_fun() 對這三個變量做讀操作,因爲沒考慮到void CreatePoints(date<int> *buff,const int& n);  函數創建數組時候還要做寫操作,所以函數定義爲const,當然這也避免了誤操作。所以我在創建數組做寫操作的時候定義爲了友元  friend void CreatePoints(date<int> *buff, const int& n);   ,友元是可以訪問private的,就不用訪問受限的 year_fun()  month_fun()  day_fun()  。還考慮到類型一定會有賦值,而賦值運算符因爲格式原因會用到this指針,所以我把賦值重載和賦值操作的函數放到了類內。然後date類定義初始化問題,我採用了構造函數方式,並把默認值設爲 1年1月1日,操作採用了構造函數獨有的方式  : year(y), month(m), day(d)  ,並且我考慮通用性我定義了一個模板 T    

 template<typename T> //def template typename is T   

這樣類就定義好了,下面看一下類完整代碼

 

#include<iostream>
#include<random>		//c++ 11  裏面不少隨機數生成器的引擎和分佈只支持c++ 11標準,部分編譯器不支持
using namespace std;
template<typename T>	//def template typename is T

class date
{
public:
	date(T y = 1, T m = 1, T d = 1) : year(y), month(m), day(d)
	{
		;
	}
	T year_fun()   const { return year; }
	T month_fun()  const { return month; }
	T day_fun()    const { return day; }
	date& operator = (const date&);	//賦值運算符重載函數


private:
	T year, month, day;		//def 年,月,日

	friend date& __doequ(date*, const date&);	//賦值實際運算函數
	friend void CreatePoints(date<int> *buff, const int& n);

};

然後我爲了儘快的看到測驗類有沒有出問題,我重載了 << 運算符

 

 

/*	對 cout<< date 類型的重載    ostream 是 cout的類	*/
ostream& operator<<(ostream& os, const date<int>& x)
{
	return os << x.year_fun() << '-' << x.month_fun() << '-' << x.day_fun();
}

然後我在cpp中進行測試
 

#include<iostream>
#include"date.h"
#include<windows.h>
using namespace std;		//這句話其實就表示了所有的標準庫函數都在標準命名空間std中進行了定義,其作用就在於避免發生重命名的問題。(本質是把std中定義的函數、變量、類等釋放出來)
int main(void)
{
	//date<int> d1(2017, 4, 15);
	//date<int> d2(2017, 4, 16);
	//date<int> d3(2017, 4, 17);
	date<int> d1{ 2017, 4, 15 };		//一般情況下{}和()都可以做初始化操作,而前着明確了要做什麼(初始化),避免了一些潛在的錯誤,當然這裏()也是可以的。
	date<int> d2{ 2017, 4, 16 };
	date<int> d3{ 2017, 4, 17 };
	cout << "d1: " << d1 << endl;
	system("pause");
	return 0;
}

因爲類型就肯定有賦值,我馬上把賦值運算符重載和賦值操作的函數寫了一下。在這裏稍微解釋一下操作符的重載,重載故名思意也就是這個操作符已經定義好了,只不過原來它只支持標準庫的類型,讓你支持新定義的類型,對新定義的類型實現操作符基本含義的操作。那麼date類型的賦值其實就是把年月日分別賦值,而年月日一般是 int 類型,而賦值操作支持int 類型,那麼這個函數就很容易的寫出來了,因爲格式原因需要用this指針,return 中調用其他實際賦值的函數。在寫重載函數需要注意的是因爲它是成員函數,沒有在類內定義所以需要在函數前寫  date<int>::      。

 

 

/*	日期	賦值(=)	實際運算函數	*/
inline date<int>&
__doequ(date<int>* ths, const date<int>& r)
{
	ths->year = r.year;
	ths->month = r.month;
	ths->day = r.day;
	return *ths;
}

/*	函數名 ooerator=	日期賦值(=)	*/
inline date<int>&
date<int>::operator=(const date<int>& r)
{
	return __doequ(this, r);
}

/**************************************************************/


然後我進行了測試,測試代碼如下(代碼是在main()函數中)

 

 

	cout << "d1: " << d1 << endl;
	d1 = date<int>(2015, 10, 1);	//測試賦值運算符重載
	cout << "測試賦值運算符重載:	d1 = date<int>(2015, 10, 1);	d1:" << d1 << endl;
cout << "測試賦值運算符重載:	d1 = date<int>(2015, 10, 1);	d1:" << d1 << endl;

 

 

 

 

要求中需要寫一個print()函數,因爲有了之前的 << 重載,所以我馬上想到了傳參數,把要打印的對象(類變量)傳進來打印,於是就有了

 

/*		print函數實現方法一(字符串類型輸出,date類型的輸出)		*/
inline void
printA(const date<int>& x)
{
	cout << x << endl;
}

測試:

 

 

	printA(d1);												//printA函數  使用  方法一(date類型的輸)

因爲之前的代碼不光打印對象值,還會打印一段註釋性質的話,我由此想到了一個入門級函數printf()函數。我們都知道printf函數第一段一般有個雙引號,後面是變量。變量很好辦第二個參數就寫

const date<int>& x

那麼雙引號是什麼呢?

 

一段話在程序中又是什麼類型呢?

對,字符串。

而%d的處理先不管他,以後的博客裏我會實現它。那我就定義爲了 char*  類型來接受字符串,那一個變種的print()函數就出來了。

 

inline void
printA(const char *a,const date<int>& x)
{
	cout << a << x << endl;
}

測試:

 

 

	printA("(使用自定義printA) d1:", d1);					//printA函數  使用  方法一(字符串類型輸出,date類型的輸出)

然後我發現這函數並不怎麼好用,它只能前面接受字符串後面接受date類型的對象。並不能作爲一個通用型的輸出函數,我想在任何位置傳入任意類型的數據,讓print函數給我打印出來,甚至新定義的類型,我想輸入無限個參數,給我打印出來。這瘋狂的想法,讓我迎來了不斷出錯,耗時巨大的print實現之旅。首先我詢問了助教老師,老師給我一個不錯的方向,然後我就開始百度,及時百度上有不錯的程序,還是讓我出了無數的bug,最好因爲時間關係換行部分還沒有隨心所欲的輸出功能(轉義字符的支持),其他部分基本可以實現printf()函數的功能了。類型方面的思路其實就是模板的一種應用,而變參其實就是c++11 的一個新功能(可能有的編譯器並不支持新標準):變長模板,包擴展,這個功能我會在最後附上百度鏈接,這裏就不展開了。它用的了遞歸的思想,而遞歸地結束用到了函數重載的思想,定義了一個空函數。因爲之前寫了一個錯誤的聲明,所以造成print重名問題,所以我把print函數放到了命名空間guo中。

 

 

/**************************************************************/

/*						print函數實現方法二						*/
//void printX() {
//
//}
//
//template <typename T1, typename... Types>
//void printX(const T1& firstArg, const Types&... args)
//{
//	cout << firstArg << endl;
//	printX(args...);
//}

namespace guo{												//把函數封裝到命名空間guo ,防止裏面的函數、變量、類、對象重複定義
	void print()
	{
		;
	}

	template<class type, class... types>		//class 的定義和 typename 類似 都是說這個模板參數定義的是一個類,而不是變量.class type 和	class... types      type 和 types 是模式 , ...  是包擴展
	void print(const type& x, const types&... next)
	{
		cout << x << endl;
		print(next...);
	}

}

測試:

	guo::print("(guo::使用自定義print):", d1, d2, d3);		//print函數   使用	方法二	(支持任意類型且參數變長)   (		引用命名///空間guo中的print()函數,這樣可以和命名空間std中的print();  起到區分作用		)

然後就是創建10個隨機日期的數組函數  void CreatePoints(date<int> *buff,const int& n);   ,我查了不少資料 rand() 這個線性的僞隨機數函數和srand()和time() 這個系統隨機事件生成的隨機數。查了寫資料,覺得借鑑c++之父寫個一個隨機數生成器,運用了c++11標準的引擎和分佈,一個隨機數生成器就出來了。這個隨機數生成器雖然安全可以限定範圍,但是本質上還是一個僞隨機數生成器,如果利用容器,寫個尺度可以解決這個問題。因爲時間關係,沒有去學習這些新東西,在後續的博客裏我會繼續完善它。

其中用到了標準庫的引擎和分佈,應該包含它。

 

#include<random>		//c++ 11  裏面不少隨機數生成器的引擎和分佈只支持c++ 11標準,部分編譯器不支持

 

 

 

 


 
class Rand_int				//等概率整數的隨機數生成器		隨機生成器由引擎(負責生成一組隨機值或者僞隨機數)和一種分佈(負責把引擎產生的值映射到某個數學分佈上)
{
public:
	Rand_int(int low, int high) : dist{ low, high }
	{
		;
	}	 		//構造函數  初始化隨機數範圍
	int operator()(){ return dist(re); }		//操作符() 重載	得到一個隨機int
private:
	default_random_engine re;		//默認隨機引擎
	uniform_int_distribution<> dist;//分佈:生成的所有整數概率相等

};


然後在 void CreatePoints(date<int> *buff,const int& n);     函數中我用了簡單的瑞年月份算法,給不同的月份給予不同的隨機數生成器。

 

 

void CreatePoints(date<int> *buff,const int& n)
{
	Rand_int rnd_year{ 1, 2100 }, rnd_month{ 1, 12 }, rnd_day_28{ 1, 28 }, rnd_day_29{ 1, 29 }, rnd_day_30{ 1, 30 }, rnd_day_31{ 1, 31 };
	for (int i = 0; i < n; i++)
	{
		buff[i].year = rnd_year();
		buff[i].month = rnd_month();
		if (buff[i].month == 2)					//2月份
		{
			if (buff[i].year % 400 == 0 || (buff[i].year % 4 == 0 && buff[i].year % 100 != 0))	//是瑞年
			{
				buff[i].day = rnd_day_29();
			}
			else                                                                                 //不是瑞年
			{
				buff[i].day = rnd_day_28();
			}
		}
		else if (buff[i].month == 1 || buff[i].month == 3 || buff[i].month == 5 || buff[i].month == 7 || buff[i].month == 8 || buff[i].month == 10 || buff[i].month ==12 )	//31天月份	(else減少判斷次數,在cpu運算速度慢時(如89c51單片機),如果判斷過多甚至可能卡死現象)
		{
			buff[i].day = rnd_day_31();
		}
		else if (buff[i].month == 4 || buff[i].month == 6 || buff[i].month == 9 || buff[i].month == 11)	//30天月份		(如果直接else,出錯可能性會大)
		{
			buff[i].day = rnd_day_30();
		}
	}
}

測試:

 

 

#include<iostream>
#include"date.h"
#include<windows.h>
#include<random>		//c++ 11  裏面不少隨機數生成器的引擎和分佈只支持c++ 11標準,部分編譯器不支持
const int SUM = 10;		//C語言中常用的方法是	#define SUM 10   而這種方法在程序運行過程中可能出現各種錯誤,如不做類型檢查等,所以我採用了宏常量定義const,增加代碼的安全性

using namespace std;		//這句話其實就表示了所有的標準庫函數都在標準命名空間std中進行了定義,其作用就在於避免發生重命名的問題。(本質是把std中定義的函數、變量、類等釋放出來)
//using namespace guo;		//不能偷懶用這種全局的命名空間聲明,因爲print();在命名空間std和命名空間guo中都有定義。( 因爲這個聲明把命名空間中的guo釋放出來以後發現有兩個print(); 從語法上會產生歧義 )


int main(void)
{
	//date<int> d1(2017, 4, 15);
	//date<int> d2(2017, 4, 16);
	//date<int> d3(2017, 4, 17);
	date<int> d1{ 2017, 4, 15 };		//一般情況下{}和()都可以做初始化操作,而前着明確了要做什麼(初始化),避免了一些潛在的錯誤,當然這裏()也是可以的。
	date<int> d2{ 2017, 4, 16 };
	date<int> d3{ 2017, 4, 17 };
	//default_random_engine e;								//隨機數方法一		默認隨機引擎
	//uniform_int_distribution<unsigned> dist(1,10000);		//分佈:生成的所有整數概率相等   (範圍1——10000)
	//int rand1 = dist(e);									////獲取僞隨機數 
	date<int> ten_rand_date[SUM];

	CreatePoints(ten_rand_date,SUM);

	cout << "d1: " << d1 << endl;
	d1 = date<int>(2015, 10, 1);	//測試賦值運算符重載
	cout << "測試賦值運算符重載:	d1 = date<int>(2015, 10, 1);	d1:" << d1 << endl;
	printA(d1);												//printA函數  使用  方法一(date類型的輸出)
	printA("(使用自定義printA) d1:", d1);					//printA函數  使用  方法一(字符串類型輸出,date類型的輸出)
	guo::print("(guo::使用自定義print):", d1, d2, d3);		//print函數   使用	方法二	(支持任意類型且參數變長)   (		引用命名空間guo中的print()函數,這樣可以和命名空間std中的print();  起到區分作用		)
	guo::print("隨機創建的10個日期爲:");
	for (int i = 0; i < SUM; i++)
	{
		guo::print(ten_rand_date[i]);
	}
	system("pause");
	return 0;
}

然後就是排序sort,排序就要用到  >   <   ==   >=   <=  重載了,我的思路是用bool類型返回運算符的真值,一種是if   else if嵌套判斷,另一種 與 或 判斷。

 

/*		函數名 ooerator==(日期,日期)	判斷兩個日期是否相等     重載		*/
inline bool
operator==(const date<int>& x, const date<int>& y)
{
	return  x.year_fun() == y.year_fun() && x.month_fun() == y.month_fun() && x.day_fun()==y.day_fun();
}


/*		函數名 ooerator>(日期,日期)	判斷 x日期 是否大於 y日期     重載		*/
bool operator>(const date<int>& x, const date<int>& y)
{
	bool feedback;	//定義反饋變量
	if (x.year_fun() > y.year_fun())
	{
		feedback = 1;
	}
	else if (x.year_fun() < y.year_fun())
	{
		feedback = 0;
	}
	else if (x.year_fun() == y.year_fun())
	{
		if (x.month_fun() > y.month_fun())
		{
			feedback = 1;
		}
		else if (x.month_fun() < y.month_fun())
		{
			feedback = 0;
		}
		else if (x.month_fun() == y.month_fun())
		{
			if (x.day_fun() > y.day_fun())
			{
				feedback = 1;
			}
			else if (x.day_fun() < y.day_fun())
			{
				feedback = 0;
			}
			else if (x.day_fun() == y.day_fun())
			{
				feedback = 0;
			}
		}
	}
	return feedback;
}


/*		函數名 ooerator>=(日期,日期)	判斷 x日期 是否大於等於 y日期     重載		*/
bool operator>=(const date<int>& x, const date<int>& y)
{
	bool feedback;	//定義反饋變量
	if (x.year_fun() >= y.year_fun())
	{
		feedback = 1;
	}
	else if (x.year_fun() < y.year_fun())
	{
		feedback = 0;
	}
	return feedback;
}


/*		函數名 ooerator<(日期,日期)	判斷 x日期 是否小於 y日期     重載		*/
bool operator<(const date<int>& x, const date<int>& y)
{
	bool feedback;	//定義反饋變量
	if (x.year_fun() > y.year_fun())
	{
		feedback = 0;
	}
	else if (x.year_fun() < y.year_fun())
	{
		feedback = 1;
	}
	else if (x.year_fun() == y.year_fun())
	{
		if (x.month_fun() > y.month_fun())
		{
			feedback = 0;
		}
		else if (x.month_fun() < y.month_fun())
		{
			feedback = 1;
		}
		else if (x.month_fun() == y.month_fun())
		{
			if (x.day_fun() > y.day_fun())
			{
				feedback = 0;
			}
			else if (x.day_fun() < y.day_fun())
			{
				feedback = 1;
			}
			else if (x.day_fun() == y.day_fun())
			{
				feedback = 0;
			}
		}
	}
	return feedback;
}


/*		函數名 ooerator<=(日期,日期)	判斷 x日期 是否小於等於 y日期     重載		*/
bool operator<=(const date<int>& x, const date<int>& y)
{
	bool feedback;	//定義反饋變量
	if (x.year_fun() > y.year_fun())
	{
		feedback = 0;
	}
	else if (x.year_fun() <= y.year_fun())
	{
		feedback = 1;
	}
	return feedback;
}

測試:

 

	guo::print("==運算符測試:d1,d2",d1==d2);	// 0
	guo::print("==運算符測試:d1,d1", d1 == d1);// 1

	guo::print(">運算符測試:d1,d2", d1 > d2);	// 0
	guo::print(">運算符測試:d1,d1", d1 > d1); // 0
	guo::print(">運算符測試:d2,d1", d2 > d1);	// 1

	guo::print("<運算符測試:d1,d2", d1 < d2);	// 1
	guo::print("<運算符測試:d1,d1", d1 < d1); // 0
	guo::print("<運算符測試:d2,d1", d2 < d1);	// 0

	guo::print(">=運算符測試:d1,d2", d1 >= d2);	// 0
	guo::print(">=運算符測試:d1,d1", d1 >= d1);	// 1
	guo::print(">=運算符測試:d2,d1", d2 >= d1);	// 1

	guo::print("<=運算符測試:d1,d2", d1 <= d2);	// 1
	guo::print("<=運算符測試:d1,d1", d1 <= d1);    // 1
	guo::print("<=運算符測試:d2,d1", d2 <= d1);	// 0

 

 

然後就是sort()函數了,我瞭解中sort在標準庫中也有類似的函數,但是好像封裝到SLT中,我沒有看到。sort本質是個排序函數,他有很多種形式,因爲之前已經把date類給封裝起來了,所以現在數組內元素的排序和標準庫中定義的類型是操作是一樣的,如:int 。

大家最常用的算法是冒泡排序,其次是選擇排序,直接插入排序。最簡單的是冒泡排序,但是時間複雜度比較高,小數據的話直接插入排序比較好。但是我考慮到是對date類操作函數,我充分考慮到了擴展性。我採取了 快速排序 這種算法,我覺得這種算法對作爲date類的排序算法比較好。

 

 

/*								快速排序算法QuickSort										*/
int partition(date<int> *arr, int low, int high)	//快速排序一次劃分算法partition
{
	date<int> key;
	key = arr[low];
	while (low<high)
	{
		while (low <high && arr[high] >= key)		//右側掃描
		{
			high--;
		}
		if (low < high)
		{
			arr[low++] = arr[high];				//將較小的記錄交換到前面
		}
		while (low < high && arr[low] <= key)		//左側掃描
		{
			low++;
		}
		if (low < high)
		{
			arr[high--] = arr[low];					//將較大的記錄交換到後面
		}
	}
	arr[low] = key;
	return low;										//low爲軸值記錄的最終位置
}

void sort(date<int> *arr, int start, int end)		//快速排序算法QuickSort
{
	int pos;
	if (start<end)									//遞歸結束
	{
		pos = partition(arr, start, end);			//一次劃分,pos爲軸值得最終位置
		sort(arr, start, pos - 1);					//遞歸地對左側子序列進行快速排序
		sort(arr, pos + 1, end);					//遞歸地對右側子序列進行快速排序
	}

}
/****************************************************************************************************/

 

測試:

 

	sort(ten_rand_date, 0, SUM - 1);	//排序函數   (快速排序算法QuickSort)
	guo::print("排序後的10個日期爲:");
	for (int i = 0; i < SUM; i++)
	{
		guo::print(ten_rand_date[i]);
	}

 

在這個測試案例和前面創建隨機日期測試案例中都使用了SUM,這裏的SUM不是#define 出來的,而是用了更安全的const這種宏常量定義。

 

const int SUM = 10;		//C語言中常用的方法是	#define SUM 10   而這種方法在程序運行過程中可能出現各種錯誤,如不做類型檢查等,所以我採用了宏常量定義const,增加代碼的安全性

using namespace std;		//這句話其實就表示了所有的標準庫函數都在標準命名空間std中進行了定義,其作用就在於避免發生重命名的問題。(本質是把std中定義的函數、變量、類等釋放出來)

 

  實際上如果針對date類而言有一種算法比較簡單,但是通用性上還得花點心思去完善。這種算法是我和羣裏交流的時候,羣裏有個人寫的算法,這種算法讓我眼前一亮。我大概提供一下我理解的思路。這種算法是把年和月做乘法,然後在做加法,合成一個 long int 類型。   (年*10000)+(月*100)+日  ,可以把每個日期轉換成這種形式,然後直接比較大小,這種思路還是非常不錯的。
  到此整個dete類就寫好了,如果有不足之處歡迎大家指正。

  本文涉及知識的鏈接:

(1) http://blog.csdn.net/wang_hong_jun/article/details/6994508

(2) http://blog.csdn.net/abc5382334/article/details/18052757

(3) http://www.cnblogs.com/uniqueliu/archive/2011/07/10/2102238.html

(4) http://dev.yesky.com/13/2221013.shtml

(5) http://www.cnblogs.com/zenny-chen/archive/2013/02/03/2890917.html

(6) https://zhidao.baidu.com/question/1830777199405373780.htmldevice=mobile&ssid=0&from=1099b&uid=0&pu=usm@0,sz@1320_2001,ta@iphone_1_9.3_3_60&bd_page_type=1&baiduid=E486CE3EC785D4FB6ECFDF17207635B6&tj=www_zhidao_normal_1_0_10_l1

(7) http://blog.csdn.net/tsbyj/article/details/46994851

(8) http://klqulei23.blog.163.com/blog/static/1323153372012722111721750/

  最後放一下完整版的代碼:

 

//	本文件名  date.h			我的編程環境VS2013
/*		本程序的註釋代表僅代表個人理解,不一定完全正確,全是我親自敲上去的,如有錯誤請聯繫我。						*/
/*	本程序隨機數生成器類和變參數print()函數使用了C++11 的標準,可能有的編譯器不能編譯通過,我會截圖一下編譯結果	*/



#ifndef __DATE_H__
#define __DATE_H__

#include<iostream>
#include<random>		//c++ 11  裏面不少隨機數生成器的引擎和分佈只支持c++ 11標準,部分編譯器不支持
using namespace std;
template<typename T>	//def template typename is T

class date
{
public:
	date(T y = 0, T m = 0, T d = 0) : year(y), month(m), day(d)
	{
		;
	}
	T year_fun()   const { return year; }
	T month_fun()  const { return month; }
	T day_fun()    const { return day; }
	date& operator = (const date&);	//賦值運算符重載函數


private:
	T year, month, day;		//def 年,月,日

	friend date& __doequ(date*, const date&);	//賦值實際運算函數
	friend void CreatePoints(date<int> *buff, const int& n);

};

class Rand_int				//等概率整數的隨機數生成器		隨機生成器由引擎(負責生成一組隨機值或者僞隨機數)和一種分佈(負責把引擎產生的值映射到某個數學分佈上)
{
public:
	Rand_int(int low, int high) : dist{ low, high }
	{
		;
	}	 		//構造函數  初始化隨機數範圍
	int operator()(){ return dist(re); }		//操作符() 重載	得到一個隨機int
private:
	default_random_engine re;		//默認隨機引擎
	uniform_int_distribution<> dist;//分佈:生成的所有整數概率相等

};

/*										函數聲明區															*/
//inline date<int> printA(const char *a, const date<int>& x);
inline date<int>& __doequ(date<int>* ths, const date<int>& r);	//賦值實際運算函數


/************************************************************************************************************/

/*	對 cout<< date 類型的重載    ostream 是 cout的類	*/
ostream& operator<<(ostream& os, const date<int>& x)
{
	return os << x.year_fun() << '-' << x.month_fun() << '-' << x.day_fun();
}


/*		print函數實現方法一(字符串類型輸出,date類型的輸出)		*/
inline void
printA(const date<int>& x)
{
	cout << x << endl;
}

inline void
printA(const char *a,const date<int>& x)
{
	cout << a << x << endl;
}
/**************************************************************/

/*						print函數實現方法二						*/
//void printX() {
//
//}
//
//template <typename T1, typename... Types>
//void printX(const T1& firstArg, const Types&... args)
//{
//	cout << firstArg << endl;
//	printX(args...);
//}

namespace guo{												//把函數封裝到命名空間guo ,防止裏面的函數、變量、類、對象重複定義
	void print()
	{
		;
	}

	template<class type, class... types>		//class 的定義和 typename 類似 都是說這個模板參數定義的是一個類,而不是變量.class type 和	class... types      type 和 types 是模式 , ...  是包擴展
	void print(const type& x, const types&... next)
	{
		cout << x << endl;
		print(next...);
	}

}

/*	日期	賦值(=)	實際運算函數	*/
inline date<int>&
__doequ(date<int>* ths, const date<int>& r)
{
	ths->year = r.year;
	ths->month = r.month;
	ths->day = r.day;
	return *ths;
}

/*	函數名 ooerator=	日期賦值(=)	*/
inline date<int>&
date<int>::operator=(const date<int>& r)
{
	return __doequ(this, r);
}

/**************************************************************/

void CreatePoints(date<int> *buff,const int& n)
{
	Rand_int rnd_year{ 1, 2100 }, rnd_month{ 1, 12 }, rnd_day_28{ 1, 28 }, rnd_day_29{ 1, 29 }, rnd_day_30{ 1, 30 }, rnd_day_31{ 1, 31 };
	for (int i = 0; i < n; i++)
	{
		buff[i].year = rnd_year();
		buff[i].month = rnd_month();
		if (buff[i].month == 2)					//2月份
		{
			if (buff[i].year % 400 == 0 || (buff[i].year % 4 == 0 && buff[i].year % 100 != 0))	//是瑞年
			{
				buff[i].day = rnd_day_29();
			}
			else                                                                                 //不是瑞年
			{
				buff[i].day = rnd_day_28();
			}
		}
		else if (buff[i].month == 1 || buff[i].month == 3 || buff[i].month == 5 || buff[i].month == 7 || buff[i].month == 8 || buff[i].month == 10 || buff[i].month ==12 )	//31天月份	(else減少判斷次數,在cpu運算速度慢時(如89c51單片機),如果判斷過多甚至可能卡死現象)
		{
			buff[i].day = rnd_day_31();
		}
		else if (buff[i].month == 4 || buff[i].month == 6 || buff[i].month == 9 || buff[i].month == 11)	//30天月份		(如果直接else,出錯可能性會大)
		{
			buff[i].day = rnd_day_30();
		}
	}
}

/*		函數名 ooerator==(日期,日期)	判斷兩個日期是否相等     重載		*/
inline bool
operator==(const date<int>& x, const date<int>& y)
{
	return  x.year_fun() == y.year_fun() && x.month_fun() == y.month_fun() && x.day_fun()==y.day_fun();
}

/*		函數名 ooerator>(日期,日期)	判斷 x日期 是否大於 y日期     重載		*/
bool operator>(const date<int>& x, const date<int>& y)
{
	bool feedback;	//定義反饋變量
	if (x.year_fun() > y.year_fun())
	{
		feedback = 1;
	}
	else if (x.year_fun() < y.year_fun())
	{
		feedback = 0;
	}
	else if (x.year_fun() == y.year_fun())
	{
		if (x.month_fun() > y.month_fun())
		{
			feedback = 1;
		}
		else if (x.month_fun() < y.month_fun())
		{
			feedback = 0;
		}
		else if (x.month_fun() == y.month_fun())
		{
			if (x.day_fun() > y.day_fun())
			{
				feedback = 1;
			}
			else if (x.day_fun() < y.day_fun())
			{
				feedback = 0;
			}
			else if (x.day_fun() == y.day_fun())
			{
				feedback = 0;
			}
		}
	}
	return feedback;
}

/*		函數名 ooerator>=(日期,日期)	判斷 x日期 是否大於等於 y日期     重載		*/
bool operator>=(const date<int>& x, const date<int>& y)
{
	bool feedback;	//定義反饋變量
	if (x.year_fun() >= y.year_fun())
	{
		feedback = 1;
	}
	else if (x.year_fun() < y.year_fun())
	{
		feedback = 0;
	}
	return feedback;
}

/*		函數名 ooerator<(日期,日期)	判斷 x日期 是否小於 y日期     重載		*/
bool operator<(const date<int>& x, const date<int>& y)
{
	bool feedback;	//定義反饋變量
	if (x.year_fun() > y.year_fun())
	{
		feedback = 0;
	}
	else if (x.year_fun() < y.year_fun())
	{
		feedback = 1;
	}
	else if (x.year_fun() == y.year_fun())
	{
		if (x.month_fun() > y.month_fun())
		{
			feedback = 0;
		}
		else if (x.month_fun() < y.month_fun())
		{
			feedback = 1;
		}
		else if (x.month_fun() == y.month_fun())
		{
			if (x.day_fun() > y.day_fun())
			{
				feedback = 0;
			}
			else if (x.day_fun() < y.day_fun())
			{
				feedback = 1;
			}
			else if (x.day_fun() == y.day_fun())
			{
				feedback = 0;
			}
		}
	}
	return feedback;
}

/*		函數名 ooerator<=(日期,日期)	判斷 x日期 是否小於等於 y日期     重載		*/
bool operator<=(const date<int>& x, const date<int>& y)
{
	bool feedback;	//定義反饋變量
	if (x.year_fun() > y.year_fun())
	{
		feedback = 0;
	}
	else if (x.year_fun() <= y.year_fun())
	{
		feedback = 1;
	}
	return feedback;
}

/*								快速排序算法QuickSort										*/
int partition(date<int> *arr, int low, int high)	//快速排序一次劃分算法partition
{
	date<int> key;
	key = arr[low];
	while (low<high)
	{
		while (low <high && arr[high] >= key)		//右側掃描
		{
			high--;
		}
		if (low < high)
		{
			arr[low++] = arr[high];				//將較小的記錄交換到前面
		}
		while (low < high && arr[low] <= key)		//左側掃描
		{
			low++;
		}
		if (low < high)
		{
			arr[high--] = arr[low];					//將較大的記錄交換到後面
		}
	}
	arr[low] = key;
	return low;										//low爲軸值記錄的最終位置
}

void sort(date<int> *arr, int start, int end)		//快速排序算法QuickSort
{
	int pos;
	if (start<end)									//遞歸結束
	{
		pos = partition(arr, start, end);			//一次劃分,pos爲軸值得最終位置
		sort(arr, start, pos - 1);					//遞歸地對左側子序列進行快速排序
		sort(arr, pos + 1, end);					//遞歸地對右側子序列進行快速排序
	}

}
/****************************************************************************************************/

#endif

 
// 本文件名 date.cpp 我的編程環境VS2013 /* 本程序的註釋代表僅代表個人理解,不一定完全正確,全是我親自敲上去的,如有錯誤請聯繫我。 */ /* 本程序隨機數生成器類和變參數print()函數使用了C++11 的標準,可能有的編譯器不能編譯通過,我會截圖一下編譯結果 */ #include<iostream> #include"date.h" #include<windows.h> #include<random> //c++ 11 裏面不少隨機數生成器的引擎和分佈只支持c++ 11標準,部分編譯器不支持 const int SUM = 10; //C語言中常用的方法是 #define SUM 10 而這種方法在程序運行過程中可能出現各種錯誤,如不做類型檢查等,所以我採用了宏常量定義const,增加代碼的安全性 using namespace std; //這句話其實就表示了所有的標準庫函數都在標準命名空間std中進行了定義,其作用就在於避免發生重命名的問題。(本質是把std中定義的函數、變量、類等釋放出來) //using namespace guo; //不能偷懶用這種全局的命名空間聲明,因爲print();在命名空間std和命名空間guo中都有定義。( 因爲這個聲明把命名空間中的guo釋放出來以後發現有兩個print(); 從語法上會產生歧義 ) int main(void) { //date<int> d1(2017, 4, 15); //date<int> d2(2017, 4, 16); //date<int> d3(2017, 4, 17); date<int> d1{ 2017, 4, 15 }; //一般情況下{}和()都可以做初始化操作,而前着明確了要做什麼(初始化),避免了一些潛在的錯誤,當然這裏()也是可以的。 date<int> d2{ 2017, 4, 16 }; date<int> d3{ 2017, 4, 17 }; //default_random_engine e; //隨機數方法一 默認隨機引擎 //uniform_int_distribution<unsigned> dist(1,10000); //分佈:生成的所有整數概率相等 (範圍1——10000) //int rand1 = dist(e); ////獲取僞隨機數 date<int> ten_rand_date[SUM]; CreatePoints(ten_rand_date,SUM); cout << "d1: " << d1 << endl; d1 = date<int>(2015, 10, 1); //測試賦值運算符重載 cout << "測試賦值運算符重載: d1 = date<int>(2015, 10, 1); d1:" << d1 << endl; printA(d1); //printA函數 使用 方法一(date類型的輸出) printA("(使用自定義printA) d1:", d1); //printA函數 使用 方法一(字符串類型輸出,date類型的輸出) guo::print("(guo::使用自定義print):", d1, d2, d3); //print函數 使用 方法二 (支持任意類型且參數變長) ( 引用命名空間guo中的print()函數,這樣可以和命名空間std中的print(); 起到區分作用 ) guo::print("隨機創建的10個日期爲:"); for (int i = 0; i < SUM; i++) { guo::print(ten_rand_date[i]); } sort(ten_rand_date, 0, SUM - 1); //排序函數 (快速排序算法QuickSort) guo::print("排序後的10個日期爲:"); for (int i = 0; i < SUM; i++) { guo::print(ten_rand_date[i]); } guo::print("==運算符測試:d1,d2",d1==d2); // 0 guo::print("==運算符測試:d1,d1", d1 == d1);// 1 guo::print(">運算符測試:d1,d2", d1 > d2); // 0 guo::print(">運算符測試:d1,d1", d1 > d1); // 0 guo::print(">運算符測試:d2,d1", d2 > d1); // 1 guo::print("<運算符測試:d1,d2", d1 < d2); // 1 guo::print("<運算符測試:d1,d1", d1 < d1); // 0 guo::print("<運算符測試:d2,d1", d2 < d1); // 0 guo::print(">=運算符測試:d1,d2", d1 >= d2); // 0 guo::print(">=運算符測試:d1,d1", d1 >= d1); // 1 guo::print(">=運算符測試:d2,d1", d2 >= d1); // 1 guo::print("<=運算符測試:d1,d2", d1 <= d2); // 1 guo::print("<=運算符測試:d1,d1", d1 <= d1); // 1 guo::print("<=運算符測試:d2,d1", d2 <= d1); // 0 system("pause"); return 0; }

 

 


 

 

 


 

 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章