Halo,這里是Ppeua。平時主要更新C語言,C++,數(shù)據(jù)結(jié)構(gòu)算法…感興趣就關(guān)注我吧!你定不會失望。

0.運算符重載
C++中為了增強代碼的可讀性,加入了運算符的重載,與其他函數(shù)重載一樣。其命名格式如下:
返回值類型 operator操作符(參數(shù)列表)
Date operator<(Date&d1)
但并不是所有運算符都可以重載的:
- 不可以自定義出一個全新的操作符,如@
- .* :: sizeof ?: . 以上操作符不能重載,特別是第一個.*
接下來我們定義一個日期類來作為操作符重載的實踐:
class Date{
public:
Date(int day=1,int month=1,int year=1)
{
if(month>0&&month<13&&day>0&&day<getmonth(year,month))
_day=day,_month=month,_year=year;
else
cout<<"輸入錯誤";
}
Date(const Date&d)
{
_day=d._day;
_month=d._month;
_year=d._year;
cout<<"copy";
}
private:
int _year;
int _month;
int _day;
}
這是一個最基本的類函數(shù),其包括一個默認構(gòu)造函數(shù),以及一個拷貝構(gòu)造函數(shù),拷貝構(gòu)造函數(shù)會在調(diào)用時輸出:“copy”,這將有助于我們后期分析函數(shù).
1.賦值運算符 = 重載
先來看看他是怎么定義的吧.同樣符合上方的定義法則:
Date& operator=(const Date&d)
{
_day=d._day;
_month=d._month;
_year=d._year;
return *this;
}
為什么需要返回值調(diào)用呢?因為這樣做可以節(jié)省空間。以引用的方式傳參、返回值不需要調(diào)用拷貝構(gòu)造函數(shù)
那this不是形式參數(shù)嘛?為什么出作用域還能被引用呢?*this本身是形式參數(shù),但是 this指向的是date對象,其棧幀存儲在Main函數(shù)中,所以this會被銷毀,而返回的是this指向的對象,并不會被銷毀
加入const只是一個保險,讓我們不會改變傳入的參數(shù)
我們可以以一段代碼來演示一下:
class Date{
public:
Date(int day=1,int month=1,int year=1)
{
if(month>0&&month<13&&day>0&&day<getmonth(year,month))
_day=day,_month=month,_year=year;
else
cout<<"輸入錯誤";
}
Date(const Date &d)
{
_day=d._day;
_month=d._month;
_year=d._year;
cout<<"copy";
}
Date& operator=(const Date &d)
{
_day=d._day;
_month=d._month;
_year=d._year;
return *this;
}
int main()
{
Date d1(2,5,2024);
Date d2;
d2=d1;
}
先將兩個引用都去掉,運行會發(fā)現(xiàn),調(diào)用了兩次拷貝構(gòu)造,分別為傳值以及返回值:
**加上后則不會調(diào)用拷貝構(gòu)造,所以這樣寫節(jié)省空間與時間。**在編譯階段,改代碼會被轉(zhuǎn)變成
d2.operator=(d1)
這也方便我們對這個操作符的具體執(zhí)行方式進行理解。
至于為什么需要返回Date類型呢?考慮以下場景:
int a;
int b;
int c;
a=b=c=1;
這段代碼運行的實質(zhì)為:
所以我們需要令其返回Date類型來滿足這種情況:
int main()
{
Date d1(2,5,2024);
Date d2;
Date d3;
d3=d2=d1;
}
賦值運算符如果不自己定義,系統(tǒng)就會自動生成一個賦值運算符(這與之前提到的構(gòu)造函數(shù)、拷貝構(gòu)造函數(shù)、析構(gòu)函數(shù)),其調(diào)用規(guī)則業(yè)余之前一樣,若沒有定義則內(nèi)置類型完成淺拷貝,自定義類型調(diào)用其自定義的賦值運算符重載,所以與調(diào)用拷貝構(gòu)造函數(shù)相同,涉及空間管理的內(nèi)置類型,我們需要自己定義拷貝構(gòu)造函數(shù)。這也導致賦值運算符不能定義在全局,只能定義在類中,若在自己在全局定義一個就會和類中定義的沖突。
2.比較運算符 == 重載
bool operator==(const Date&d)const
{
if(_year==d._year&&_month==d._month&&_day==d._day)
{
return true;
}
return false;
}
三個都相同則返回true,否則返回false,這整體邏輯很好理解。這個函數(shù)并不是默認函數(shù),所以我們可以把它定義在全局,也可以把他定義在類中。
若定義在全局會出現(xiàn)類中私有成員無法訪問的問題(這之后會解決,但現(xiàn)在我們?yōu)榱艘?guī)避這個問題我們先將其放在類中)
那這個const為什么放在外面呢?這里是對this指針,也就是本體的一個修飾,因為this指針是隱式的存在,且在這個重載中我們并不會改變本體,所以對其加上修飾
this指針原來的模樣:無法改變this指針指向的內(nèi)容,但可以改變其值
Date * const this
現(xiàn)在修飾后:既無法改變this指針指向的內(nèi)容,也無法改變其值,對其起到了一個保護的作用
const Date * const this
3.比較運算符 != 重載
bool operator!=(const Date&d)const
{
if(*this==d)return false;
return true;
}
這里直接復用了上方的結(jié)果,也證明了重載運算符的強大之處hhhh
4.比較運算符 < 重載
bool operator<(const Date &d)const
{
if(_year<d._year)
{
return true;
}
if(_year==d._year&&_month<d._month)
{
return true;
}
if(_year==d._year&&_month==d._month&&_day<d._day)
{
return true;
}
return false;
}
主要為邏輯的實現(xiàn):
- 若年小,則直接返回true
- 若年相同,月小,直接返回true
- 若年月都相同,天數(shù)小,直接返回true
若不滿足以上情況,則說明不小于,則直接返回false
5.比較運算符 <= 重載
bool operator<=(const Date &d)const
{
return *this<d||*this==d;
}
也是對之前寫過的==以及<的一個復用.若小于或者等于則滿足小于等于
6. 比較運算符 > 重載
bool operator>(const Date &d)const
{
return (!(*this<d)&&(*this!=d))?true:false;
}
若不小于且不等于則滿足小于等于
7.比較運算符 >= 重載
bool operator>=(const Date &d)const
{
return !(*this<d);
}
若不小于則滿足大于等于
8. 賦值運算符 += 與 + 重載
在實現(xiàn)接下來的重載中,為了方便獲得每一個月份的具體日期,包括閏年平年的判斷。在類中實現(xiàn)了一個getmonth函數(shù)用來返回每一個月具體天數(shù)。
int getmonth(int year,int month)
{
static int monthday[13]={0,31,28,31,30,31,30,31,31,30,31,30,31};
if((year%4==0&&year%100!=0)||year%400==0)
{
if(month==2)return 29;
}
return monthday[month];
}
傳入年與月,進行判斷具體日期數(shù),其中閏年滿足四年一閏,百年不閏,四百年一閏
接下來進行+=的實現(xiàn):這里需要先判斷下若Day<0,則等價于計算*this-=Day
Date& operator+=(int Day)
{
if(Day<0)
{
*this-=(-Day);
return *this;
}
_day+=Day;
while(_day>getmonth(_year,_month))
{
_day-=getmonth(_year,_month);
++_month;
if(_month>12)
{
_month=1;
_year++;
}
}
return *this;
}
具體實現(xiàn)邏輯如下:
那么我們來看看+的邏輯:
Date operator+(int day)
{
Date tmp=*this;
tmp+=day;
tmp.print();
return tmp;
}
為了不改變Date對象的本身,需要先將Date復制一份(這里使用拷貝構(gòu)造函數(shù),而不是賦值運算符重載,因為其在初始化對象時賦值),然后復用+=,最后返回其本身.因為是局部參數(shù),所以并不能返回引用,因為tmp出作用域會被銷毀.
9.賦值運算符 -= 與 - 重載:
-=:
Date& operator-=(const int Day)
{
if(Day<0)
{
*this+=(-Day);
return *this;
}
_day-=Day;
while(_day<=0)
{
--_month;
if(_month<1)
{
_year--;
_month=12;
}
_day+=getmonth(_year,_month);
}
return *this;
}
-:
Date operator-(const int Day)
{
Date tmp=*this;
return tmp-=Day;
}
整體邏輯與加法大差不差,不過多贅述.
10. 前置++與后置++
前置++
Date& operator++()
{
*this+=1;
return *this;
}
若我想要將一個Date對象執(zhí)行++操作,則直接:++Date即可.
后置++:為了和前置++再一次發(fā)生重載,加入了一個參數(shù),這個參數(shù)在重載過程中并不會起到什么作用,僅為分辨具體是哪個重載,使用時編譯器會自己在后置++中傳入0,我們僅需正常使用即可,
Date operator++(int)
{
Date tmp=*this;
*this+=1;
return tmp;
}
在剛學習c的階段,總會看到討論關(guān)于前置++與后置++的討論,現(xiàn)在看重載就知道了,因為后置++會拷貝一份原始值,所以效率會低一些.
不過在內(nèi)置類型中,這種效率變換可以忽略不計.
11.前置–與后置–
Date& operator--()
{
*this-=1;
return *this;
}
Date operator--(int)
{
Date tmp;
*this-=1;
return tmp;
}
12.邏輯運算符-的重載
為兩個日期類間想減,計算日期間的差值
int operator-(Date &d)
{
Date max=*this;
Date min=d;
int flag=1;
if(max<min)
{
max=d;
min=*this;
flag=-1;
}
int n=0;
while(min!=max)
{
++min;
++n;
}
return n*flag;
}
先找出最大的日期,通過將最小的++與計數(shù)天數(shù)++,一步步逼近最大的天數(shù),最后返回,注意傳值需要傳引用,提高效率
13.流運算符重載
為什么在c++中cout/cin可以輸出/輸入任意內(nèi)置類型的對象,而不用帶對象類型呢?因為在c++中cout與cin也為一個流對象,其底層實現(xiàn)了多個類型的重載
此重載需要定義在全局中,若定義在類中,根據(jù)上方的轉(zhuǎn)換法則,則會變成d1<<cout,但定義在全局中,會出現(xiàn)訪問不了私有變量的情況,所以我們提供了一個友元函數(shù)的聲明.
friend ostream& operator<<(ostream& out,const Date&d);
friend istream& operator>>(istream& in, Date&d);
放在類中哪里都可以,表示這個函數(shù)是這個類的朋友,可以直接訪問其中的變量與函數(shù)(關(guān)于友元的具體含義之后會細講,)
13.1輸出流重載:
ostream& operator<<(ostream& out,const Date&d)
{
out<<"day: "<<d._day<<" month: "<<d._month<<" year: "<<d._year<<endl;
return out;
}
ostream為輸出流對象,其返回值會放進輸出流在輸出
13.2輸入流重載:
istream& operator>>(istream& in,Date&d)
{
int year, month, day;
in >> day >> month >>year;
if (month > 0 && month < 13
&& day > 0 && day <= d.getmonth(year, month))
{
d._year = year;
d._month = month;
d._day = day;
}
else
{
cout << "非法日期" << endl;
}
return in;
}
整體與上方相同
14.完整代碼:
#include<iostream>
using namespace std;
class Date{
public:
friend ostream& operator<<(ostream& out,const Date&d);
friend istream& operator>>(istream& in, Date&d);
Date(int day=1,int month=1,int year=1)
{
if(month>0&&month<13&&day>0&&day<getmonth(year,month))
_day=day,_month=month,_year=year;
else
cout<<"輸入錯誤";
}
Date(const Date&d)
{
_day=d._day;
_month=d._month;
_year=d._year;
cout<<"copy"<<endl;
}
int getmonth(int year,int month)
{
static int monthday[13]={0,31,28,31,30,31,30,31,31,30,31,30,31};
if((year%4==0&&year%100!=0)||year%400==0)
{
if(month==2)return 29;
}
return monthday[month];
}
void print()
{
cout<<"day: "<<_day<<" month: "<<_month<<" year: "<<_year<<endl;
}
Date operator=(const Date d)
{
_day=d._day;
_month=d._month;
_year=d._year;
return *this;
}
Date& operator+=(int Day)
{
if(Day<0)
{
*this-=(-Day);
return *this;
}
_day+=Day;
while(_day>getmonth(_year,_month))
{
_day-=getmonth(_year,_month);
++_month;
if(_month>12)
{
_month=1;
_year++;
}
}
return *this;
}
Date operator+(int day)
{
Date tmp=*this;
tmp+=day;
tmp.print();
return tmp;
}
Date& operator++()
{
*this+=1;
return *this;
}
Date operator++(int)
{
Date tmp=*this;
*this+=1;
return tmp;
}
int operator-(Date &d)
{
Date max=*this;
Date min=d;
int flag=1;
if(max<min)
{
max=d;
min=*this;
flag=-1;
}
int n=0;
while(min!=max)
{
++min;
++n;
}
return n*flag;
}
Date& operator-=(const int Day)
{
if(Day<0)
{
*this+=(-Day);
return *this;
}
_day-=Day;
while(_day<=0)
{
--_month;
if(_month<1)
{
_year--;
_month=12;
}
_day+=getmonth(_year,_month);
}
return *this;
}
Date operator-(const int Day)
{
Date tmp=*this;
return tmp-=Day;
}
Date& operator--()
{
*this-=1;
return *this;
}
Date operator--(int)
{
Date tmp;
*this-=1;
return tmp;
}
bool operator==(const Date&d)const
{
if(_year==d._year&&_month==d._month&&_day==d._day)
{
return true;
}
return false;
}
bool operator!=(const Date&d)const
{
if(*this==d)return false;
return true;
}
bool operator<(const Date &d)const
{
if(_year<d._year)
{
return true;
}
if(_year==d._year&&_month<d._month)
{
return true;
}
if(_year==d._year&&_month==d._month&&_day<d._day)
{
return true;
}
return false;
}
bool operator<=(const Date &d)const
{
return *this<d||*this==d;
}
bool operator>(const Date &d)const
{
return (!(*this<d)&&(*this!=d))?true:false;
}
bool operator>=(const Date &d)const
{
return !(*this<d);
}
private:
int _year;
int _month;
int _day;
};
ostream& operator<<(ostream& out,const Date&d)
{
out<<"day: "<<d._day<<" month: "<<d._month<<" year: "<<d._year<<endl;
return out;
}
istream& operator>>(istream& in,Date&d)
{
int year, month, day;
in >> day >> month >>year;
if (month > 0 && month < 13
&& day > 0 && day <= d.getmonth(year, month))
{
d._year = year;
d._month = month;
d._day = day;
}
else
{
cout << "非法日期" << endl;
}
return in;
}
int main()
{
Date d1(2,5,2024);
d1+=100;
d1.print();
}
至此日期類整體完成
15. 取地址運算符重載
class Date
{
public :
Date* operator&()
{
return this ;
}
const Date* operator&()const
{
return this ;
}
private :
int _year ;
int _month ;
int _day ;
};
默認情況下,編譯器會自動生成這個重載,大多數(shù)情況下我們也不需要寫這個重載.
若不想讓一個人獲得可修改地址,僅想讓其獲得不可修改的const地址,可以這樣寫文章來源:http://www.zghlxwxcb.cn/news/detail-446384.html
class Date
{
public :
Date* operator&()
{
return NULL ;
}
const Date* operator&()const
{
return this ;
}
private :
int _year ;
int _month ;
int _day ;
};
16.至此六個內(nèi)置類型成員函數(shù)完結(jié)
文章來源地址http://www.zghlxwxcb.cn/news/detail-446384.html
到了這里,關(guān)于【C++技能樹】令常規(guī)運算符用在類上 --類的六個成員函數(shù)II的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!