C++ 如何實(shí)現(xiàn)一個(gè)日期類(lèi)
一. 對(duì)日期類(lèi)的介紹
通過(guò)對(duì)類(lèi)和對(duì)象(這里鏈接是類(lèi)和對(duì)象的介紹)的學(xué)習(xí),類(lèi)就是一種模型一樣的東西,是對(duì)象一種抽象的描述。所以實(shí)現(xiàn)日期類(lèi),就是實(shí)現(xiàn)一個(gè)日期模型,里面有所對(duì)應(yīng)的成員變量(屬性:年/月/日),還有一些它的方法(成員函數(shù):打印該日期、對(duì)日期加減等)。
二. 實(shí)現(xiàn)日期類(lèi)
在實(shí)現(xiàn)日期類(lèi)之前,我們還需要了解一下運(yùn)算符重載,這是為了后續(xù)我們對(duì)日期的加、減天數(shù)以及日期減日期等做準(zhǔn)備,因?yàn)楫?dāng)運(yùn)算符被用于類(lèi)類(lèi)型的對(duì)象時(shí),需要我們?nèi)ブ剌d運(yùn)算符去指定新的含義。(C++規(guī)定類(lèi)類(lèi)型對(duì)象使用運(yùn)算符時(shí),必須轉(zhuǎn)換成調(diào)用對(duì)應(yīng)運(yùn)算符重載,若沒(méi)有對(duì)應(yīng)的運(yùn)算符重載,則會(huì)編譯報(bào)錯(cuò)。)
//內(nèi)置類(lèi)型 int i = 1; int j = 2; int sum = 0; i = i + 1; sum = i - j; //自定義類(lèi)型對(duì)象若想用類(lèi)似上面的就需要在類(lèi)里面聲明定義對(duì)應(yīng)的運(yùn)算符重載函數(shù)
1. 運(yùn)算符重載
C++規(guī)定類(lèi)類(lèi)型對(duì)象使用運(yùn)算符時(shí),必須轉(zhuǎn)換成對(duì)應(yīng)運(yùn)算符重載,若沒(méi)有對(duì)應(yīng)的運(yùn)算符重載,則會(huì)編譯報(bào)錯(cuò)。
也就是運(yùn)算符被用于類(lèi)類(lèi)型的對(duì)象時(shí),可以通過(guò)運(yùn)算符重載的形式指定新的含義。
運(yùn)算符重載是一個(gè)函數(shù),它的名字是由關(guān)鍵字operator和運(yùn)算符組成,也具有返回類(lèi)型、參數(shù)列表、函數(shù)體。
class A
{
public:
A(int a = 1, int b = 1)
:_a(a)
,_b(b)
{}
//這里就是賦值運(yùn)算符重載
A& operator=(const A& a)
{
_a = a._a;
_b = a._b;
return *this;
}
private:
int _a;
int _b;
};
int main()
{
A a1;
A a2(5, 2);
a1 = a2;
return 0;
}我們來(lái)查看驗(yàn)證是否按照指定新的含義進(jìn)行:
賦值之前:

賦值之后:

可以看到a1里的成員確實(shí)被a2里的成員變量賦值了。
注意:
//上述代碼中 a1 = a2; //其實(shí)詳細(xì)寫(xiě)法是: a1.operator=(a2); //但它們倆意義相同都是為了調(diào)用賦值運(yùn)算符重載//上述代碼中 a1 = a2; //其實(shí)詳細(xì)寫(xiě)法是: a1.operator=(a2); //但它們倆意義相同都是為了調(diào)用賦值運(yùn)算符重載
一元運(yùn)算符只有一個(gè)參數(shù),二元運(yùn)算符要有兩個(gè)參數(shù)。(注意:二元運(yùn)算符的左側(cè)運(yùn)算對(duì)象傳給第一個(gè)參數(shù),右側(cè)運(yùn)算對(duì)象傳給第二個(gè)參數(shù))。
也就是說(shuō),重載運(yùn)算符函數(shù)的參數(shù)個(gè)數(shù)和該運(yùn)算符作用的運(yùn)算對(duì)象數(shù)量一致的。
上面我們看到A類(lèi)重載=運(yùn)算符只寫(xiě)了一個(gè)函數(shù)參數(shù),這是因?yàn)樗浅蓡T函數(shù),第一個(gè)參數(shù)默認(rèn)傳了this指針(類(lèi)和對(duì)象有介紹)。
注意:重載運(yùn)算符必須至少有一個(gè)類(lèi)類(lèi)型的形參,不能通過(guò)運(yùn)算符重載改變內(nèi)置類(lèi)型對(duì)象的含義。
若不是成員函數(shù),就按照上面規(guī)則來(lái):
class A
{
public:
A(int a = 1, int b = 1)
:_a(a)
,_b(b)
{}
A& operator=(const A& a)
{
_a = a._a;
_b = a._b;
return *this;
}
int _a;
int _b;
};
//注意:operator + 必須至少有一個(gè)類(lèi)類(lèi)型的形參,否則會(huì)報(bào)錯(cuò),就比如下面注釋掉的情況
//int operator(int a,int n)
//{
// return a-n;
//}
int operator+(const A& a, int& n)
{
return a._a + n; //這里成員變量需要放公有,否則不能直接訪(fǎng)問(wèn)
}
int main()
{
A a;
int n = 10;
cout << a + n << endl;
return 0;
}
運(yùn)算符重載以后,優(yōu)先級(jí)和結(jié)合性與對(duì)應(yīng)的內(nèi)置類(lèi)型運(yùn)算符保持一致的。
不能重載的情況:
不能用沒(méi)有的符號(hào)創(chuàng)建新的操作符(運(yùn)算符)
//例如: operator@ //就不能創(chuàng)建
有五個(gè)運(yùn)算符是不能重載的:.* :: sizeof ?: .

后四個(gè)是經(jīng)常用到的,第一個(gè)非常用,下面單獨(dú)拿出來(lái)解釋
//函數(shù)指針較為特別,typedef類(lèi)型需要這樣寫(xiě)
typedef void (*PF)();
class A
{
public:
void func()
{
cout << "A::func()" << endl;
}
};
//若typedef成員函數(shù)指針類(lèi)型,就加個(gè)指定類(lèi)域即可
typedef void(A::*PF)();
// .*運(yùn)算符就用于以下方式回調(diào)函數(shù)的場(chǎng)景(成員函數(shù)回調(diào))
int main()
{
//成員函數(shù)要加&才能取到函數(shù)指針
PF pf = &A::func; //這里相當(dāng)于 void(A::*pf)() = &A::func;
A a;
(a.*pf)();
return 0;
}
(深入理解指針)
//這里是普通全局函數(shù)回調(diào)的形式 (*pf)();
所以這個(gè)符號(hào)的意義是對(duì)象調(diào)用成員函數(shù)指針時(shí),成員函數(shù)的回調(diào) (注意:考慮有隱含的this指針,不能顯示寫(xiě)形參和傳實(shí)參)
重載++運(yùn)算符時(shí),前置++與后置++的運(yùn)算符重載函數(shù)名都是operator++,無(wú)法來(lái)區(qū)分。 C++規(guī)定,后置++重載時(shí),增加一個(gè)int形參,跟前置++構(gòu)成函數(shù)重載,方便區(qū)分。
class Date
{
public:
//構(gòu)造函數(shù)
Date(int year = 1900,int month =1,int day=1)
:_year(year)
,_month(month)
,_day(day)
{}
//前置++
Date& operator++()
{
cout << "這里是前置++" << endl; //這里演示一下,先不實(shí)現(xiàn)
return *this;
}
//后置++
Date& operator++(int) //這里的加不加形參名都可以,必須要有int (只要整型)
{
Date tmp;
cout << "這里是后置++" << endl;
return tmp;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1;
Date d2;
++d1;
d2++;
d2.operator++(10); //這里傳不傳值都可以,因?yàn)槟莻€(gè)int一般不接收。
}
注意:重載的++運(yùn)算符也要與內(nèi)置類(lèi)型++運(yùn)算符規(guī)則一致(前置++:先++,再返回++后的結(jié)果,不產(chǎn)生拷貝;后置++:進(jìn)行++,++之后返回的是++前的結(jié)果,會(huì)產(chǎn)生拷貝。所以,一般開(kāi)以選擇前置++來(lái)減少拷貝)。
我們實(shí)現(xiàn)日期類(lèi),還想需要可以對(duì)這個(gè)日期cout和cin來(lái)方便輸出和輸入,所以<<和>>也是可以重載的,但是需要重載成全局函數(shù),重載為全局函數(shù)把ostream/istream放到第一個(gè)形參位置就可以了,第二個(gè)形參位置當(dāng)類(lèi)類(lèi)型對(duì)象。否則會(huì)有隱含的this指針,導(dǎo)致調(diào)用時(shí)變成:對(duì)象<<cout ,不符合使用習(xí)慣和可讀性(想要的是:cout<<對(duì)象)。
2.日期類(lèi)實(shí)現(xiàn)代碼
我們可以聲明定義分離來(lái)實(shí)現(xiàn),分別創(chuàng)建Date.h頭文件和Date.cpp用來(lái)定義頭文件聲明的函數(shù)。
//Date.h
#include<iostream>
#include <assert.h>
using namespace std;
class Date
{
//友元函數(shù)聲明 --這兩個(gè)全局函數(shù)就可以訪(fǎng)問(wèn)對(duì)象的私有成員
friend ostream& operator<<(ostream& out, const Date& d);
friend istream& operator>>(istream& in, Date& d);
public:
//構(gòu)造函數(shù),若沒(méi)有傳參 就是默認(rèn)構(gòu)造 全缺省函數(shù)
Date(int year = 1900, int month = 1, int day = 1);
//拷貝構(gòu)造
Date(const Date& d);
void Print()const; //打印日期
bool CheckDate() const; //檢查日期輸入是否正確
//頻繁調(diào)用的建議直接定義類(lèi)里面,默認(rèn)inline內(nèi)聯(lián)
int GetMonthDay(int year, int month)const
{
assert(month > 0 && month < 13);
static int monthDay[13] = { -1,31,28,31,30,31,30,31,31,30,31,30,31 }; //因?yàn)闆](méi)有0月,將0置-1空出來(lái)
//用static修飾是因?yàn)檫@個(gè)數(shù)組會(huì)頻繁調(diào)用,直接放在靜態(tài)區(qū)
if (month == 2 && ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0)))
{
return 29;
}
return monthDay[month];
}
bool operator<(const Date& d) const;
bool operator<=(const Date& d) const;
bool operator>(const Date& d) const;
bool operator>=(const Date& d) const;
bool operator==(const Date& d) const;
bool operator!=(const Date& d) const;
//d1 = d2
Date& operator=(const Date& d);
Date operator+(int day) const;
Date& operator+=(int day);
Date operator-(int day) const;
Date& operator-=(int day);
// d1++;
// d1.operator++(0);
Date operator++(int);
// ++d1;
// d1.operator++();
Date& operator++();
// d1--;
// d1.operator--(0);
Date operator--(int);
// --d1;
// d1.operator--();
Date& operator--();
// d1 - d2
int operator-(const Date& d) const;
//void operator<<(ostream& out);
Date* operator&() //取地址運(yùn)算符重載
//普通對(duì)象返回類(lèi)型Date*
{
return (Date*)0x2673FF40; //返回假地址
}
const Date* operator&() const //取地址運(yùn)算符重載
//const對(duì)象要調(diào)用const成員函數(shù)要返回類(lèi)型const Date*
{
return (Date*)0x2673FE30; //返回假地址
}
private:
int _year;
int _month;
int _day;
};
//重載流插入流提取
// ostream/istream類(lèi)型對(duì)象不能傳值只能傳引用,否則會(huì)報(bào)錯(cuò),因?yàn)閏++不支持它們的拷貝構(gòu)造。
//要寫(xiě)成全局函數(shù) 否則調(diào)用的時(shí)候需要這樣寫(xiě):d1<<cout d1>>cin 用著會(huì)比較別扭
//因?yàn)槿绻麑?xiě)成了成員函數(shù)第一個(gè)參數(shù)有隱藏的this指針,并且不能修改
//cout是ostream類(lèi)型的 (也可以用void返回類(lèi)型但不建議)
//cin默認(rèn)關(guān)聯(lián)cout
//在cin進(jìn)行i/o操作前會(huì)刷新cout的緩沖區(qū)
//流插入
ostream& operator<<(ostream& out, const Date& d);
//流提取
//這里第一個(gè)參數(shù)不能加const修飾了,因?yàn)樘崛〉闹狄湃掌陬?lèi)對(duì)象里
istream& operator>>(istream& in, Date& d);//Date.cpp
#include "Date.h"
bool Date::CheckDate() const//檢查日期正確
{
if (_month < 1 || _month > 12
|| _day < 1 || _day > GetMonthDay(_year, _month))
{
return false;
}
else
{
return true;
}
}
Date::Date(int year, int month, int day) //構(gòu)造函數(shù)(全缺省,前面聲明中已經(jīng)給了缺省值,定義這里就不能再寫(xiě)出來(lái)了)
{
_year = year;
_month = month;
_day = day;
if (!CheckDate())
{
cout << "非法日期:";
Print();
}
cout << endl;
}
Date::Date(const Date& d) //拷貝構(gòu)造
{
_year = d._year;
_month = d._month;
_day = d._day;
}
void Date::Print()const //打印日期
{
cout << _year << "/" << _month << "/" << _day << endl;
}
//d1 = d2
Date& Date::operator=(const Date& d)
{
if (*this != d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
return *this;
}
Date& Date::operator+=(int day) //加等可以讓自己改變
{
//先判斷day是否是負(fù)數(shù) ---- _day+(-day)==_day-day
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;
}
Date Date::operator+(int day) const //只加不能改變自己
//這里不能用傳引用返回 因?yàn)檫@里用const修飾只讀取不想改變自己,引用會(huì)把權(quán)限放大
{
Date tmp = *this;
tmp += day;
return tmp;
}
//- -=同+ +=
Date Date::operator-(int day) const
{
Date tmp = *this;
tmp -= day;
return tmp;
}
Date& Date::operator-=(int day)
{
//先判斷day是否是負(fù)數(shù) --->_day-(-day)==_day+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;
}
//d1<d2
bool Date::operator<(const Date& d) const //這里傳引用 d就是d2的別名 也就是d2的本身
{
if (_year < d._year)
{
return true;
}
else if (_year == d._year)
{
if (_month < d._month)
{
return true;
}
if (_month == d._month)
{
return _day < d._day;
}
}
return false;
}
//d1<=d2
//這里就可以復(fù)用函數(shù)了
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 (_year == d._year) && (_month == d._month) && (_day == d._day);
}
bool Date::operator!=(const Date& d) const
{
return !(*this == d);
}
// d1++; 后置++有拷貝
// d1.operator++(0);
//用的時(shí)候括號(hào)里只要是整數(shù)都可以
Date Date::operator++(int) // 加不加形參名都可以,一般不接收
{
Date tmp = *this;
*this += 1;
return tmp;
}
// ++d1; //前置++無(wú)拷貝
// d1.operator++();
Date& Date::operator++()
{
*this += 1;
return *this;
}
//-- 同理++
// d1--;
// d1.operator--(0);
Date Date::operator--(int)
{
Date tmp = *this;
*this -= 1;
return tmp;
}
// --d1;
// d1.operator--();
Date& Date::operator--()
{
*this -= 1;
return *this;
}
// d1 - d2
int Date::operator-(const Date& d) const
{
Date max = *this; //假設(shè)第一個(gè)大
Date min = d; //第二個(gè)小
int flag = 1; //等于1時(shí)表示第一個(gè)大第二個(gè)小
if (*this < d)
{
max = d;
min = *this;
flag = -1; //第二個(gè)大第一個(gè)小
}
int n = 0;
while (min != max)
{
++min;
++n;
}
return n *= flag;
}
//流插入cout<<d1<<d2
ostream& operator<<(ostream& out, const Date& d)
{
cout << d._year << "年" << d._month << "月" << d._day << "日" << endl;
return out;
}
//流提取cin>>d1>d2
istream& operator>>(istream& in, Date& d)
{
cout << "請(qǐng)依次輸入年月日:>";
in >> d._year >> d._month >> d._day;
//檢查日期是否非法
if (!d.CheckDate())
{
cout << "非法日期: " << d << "請(qǐng)重新輸入" << endl;
while (1)
{
cout << "請(qǐng)依次輸入年月日:>";
in >> d._year >> d._month >> d._day;
if (!d.CheckDate())
{
cout << "輸入日期非法:";
d.Print();
cout << "請(qǐng)重新輸入!!!" << endl;
}
else
{
break;
}
}
}
return in;
}有些注意事項(xiàng),在上面代碼的實(shí)現(xiàn)注釋中有一些解釋。
到此這篇關(guān)于C++ 如何實(shí)現(xiàn)一個(gè)日期類(lèi)的文章就介紹到這了,更多相關(guān)C++日期類(lèi)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C語(yǔ)言中循環(huán)語(yǔ)句練習(xí)實(shí)例
大家好,本篇文章主要講的是C語(yǔ)言中循環(huán)語(yǔ)句練習(xí)實(shí)例,感興趣的同學(xué)趕快來(lái)看一看吧,對(duì)你有幫助的話(huà)記得收藏一下2022-01-01
深入理解約瑟夫環(huán)的數(shù)學(xué)優(yōu)化方法
本篇文章是對(duì)約瑟夫環(huán)的數(shù)學(xué)優(yōu)化方法進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下2013-05-05
C++ 中二分查找遞歸非遞歸實(shí)現(xiàn)并分析
這篇文章主要介紹了C++ 中二分查找遞歸非遞歸實(shí)現(xiàn)并分析的相關(guān)資料,需要的朋友可以參考下2017-06-06
C/C++ 中const關(guān)鍵字的用法小結(jié)
C++中的const關(guān)鍵字的用法非常靈活,而使用const將大大改善程序的健壯性。這篇文章主要介紹了C/C++ 中const關(guān)鍵字的用法,需要的朋友可以參考下2020-02-02

