喜歡的朋友可以關注收藏一下
本文實現了一個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; }