前言:
本章將通過日期類的實(shí)現(xiàn),深入學(xué)習(xí)運(yùn)算符重載的實(shí)現(xiàn)方法。本章將完成6個(gè)默認(rèn)成員函數(shù)中剩余3個(gè)——賦值運(yùn)算符重載與取地址操作符重載的學(xué)習(xí)。
一.運(yùn)算符重載
1.運(yùn)算符重載的概念
C++
為了增強(qiáng)代碼的可讀性引入了運(yùn)算符重載,運(yùn)算符重載是具有特殊函數(shù)名的函數(shù),也具有其返回值類型,函數(shù)名字以及參數(shù)列表,其返回值類型與參數(shù)列表與普通的函數(shù)類似。
-
函數(shù)名:關(guān)鍵字
operator
后面接需要重載的運(yùn)算符符號。 - 函數(shù)原型:
返回值類型 operator操作符(參數(shù)列表)
bool operator==(Date d1,Date d2);
需要注意的是:
-
不能通過連接其他符號來創(chuàng)建新的操作符:比如
operator@
- 重載操作符必須有一個(gè)類類型參數(shù)
-
用于內(nèi)置類型的運(yùn)算符,其含義不能改變,例如:內(nèi)置的整型
+
,不 能改變其含義 - 作為類成員函數(shù)重載時(shí),其形參看起來比操作數(shù)數(shù)目少1,因?yàn)槌蓡T函數(shù)的第一個(gè)參數(shù)為隱藏的
this
-
.*
::
sizeof
?:
.
注意以上5個(gè)運(yùn)算符不能重載。這個(gè)經(jīng)常在筆試選擇題中出現(xiàn)。
2.實(shí)現(xiàn)Date類
定義一個(gè)Date類:
class Date
{
public:
//構(gòu)造函數(shù)
Date(int year = 0, int month = 0, int day = 0)
{
//判斷日期是否合法
//GetMonthDay()獲取這個(gè)月的天數(shù)
if (month > 0 && month < 13 &&
(day > 0 && day <= GetMonthDay(year, month)))
{
_year = year;
_month = month;
_day = day;
}
else
{
cout << "日期非法" << endl;
}
}
private:
int _year;//年
int _month;//月
int _day;//日
};
函數(shù)接口:
// 類里面短小函數(shù),適合做內(nèi)聯(lián)的函數(shù),直接是在類里面定義的
class Date
{
// 友元函數(shù)聲明
friend ostream& operator<<(ostream& out, const Date& d);
friend istream& operator>>(istream& in, Date& d);
public:
// 構(gòu)造
Date(int year = 0, int month = 0, int day = 0);
void Print() const;
// 當(dāng)月的天數(shù)
int GetMonthDay(int year, int month) const;
// 重載日期類與日期類運(yùn)算==, !=, <, <=, >, >=
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;
// 重載日期類與天運(yùn)算+=, +, -=, -,
Date& operator+=(int day);
Date operator+(int day) const;
Date& operator-=(int day);
Date operator-(int day) const;
// 重載日期類與日期類運(yùn)算-
int operator-(const Date& d) const;
// 賦值運(yùn)算符重載
Date& operator=(const Date& d);
// 重載日期類前置++--,后置++--
Date& operator++();
// int參數(shù) 僅僅是為了占位,跟前置重載區(qū)分
Date operator++(int);
Date& operator--();
Date operator--(int);
//取地址重載
Date* operator&();
const Date* operator&() const;
private:
int _year;
int _month;
int _day;
};
inline ostream& operator<<(ostream& out, const Date& d)
{
out << d._year << "年" << d._month << "月" << d._day << "日";
return out;
}
inline istream& operator>>(istream& in, Date& d)
{
in >> d._year >> d._month >> d._day;
return in;
}
在實(shí)現(xiàn)運(yùn)算符重載的時(shí)候,有一點(diǎn)需要特別注意:
二元運(yùn)算符的重載函數(shù)的參數(shù)有兩個(gè),規(guī)定第一個(gè)參數(shù)為左操作數(shù),第二個(gè)參數(shù)為右操作數(shù)。
在前面章節(jié)我們講過成員函數(shù)的特性,成員函數(shù)有一個(gè)自帶的參數(shù)this
,類型為類類型。所以我們可以省略第一個(gè)參數(shù),只寫第二個(gè)參數(shù)。
所以:
(1)> < >= <= != 重載
先實(shí)現(xiàn)兩個(gè)運(yùn)算符重載函數(shù),其它的就可以復(fù)用已經(jīng)實(shí)現(xiàn)好的運(yùn)算符。
class Date
{
public:
//構(gòu)造函數(shù)
//...
bool operator==(const Date& d)
{
return (_year == d._year) && (_month == d._month) && (_day == d._day);
}
bool operator<(const Date& d)
{
return _year < d._year
|| (_year == d._year && _month < d._month)
|| (_year == d._year && _month == d._month && _day < d._day);
}
bool operator<=(const Date& d)
{
//函數(shù)的復(fù)用
return *this < d || *this == d;
}
bool operator>(const Date& d)
{
//函數(shù)的復(fù)用
return !(*this <= d);
}
bool operator>=(const Date& d)
{
//函數(shù)的復(fù)用
return !(*this < d);
}
bool operator!=(const Date& d)
{
//函數(shù)的復(fù)用
return !(*this == d);
}
//...
};
(2)+= -= + - 重載
注意:下面四個(gè)運(yùn)算符重載的右操作數(shù)都為day
天數(shù)
class Date
{
public:
//...
//獲取當(dāng)月的天數(shù)
int GetMonthDay(int year, int month)
{
assert(month > 0 && month < 13);
int monthArray[13] = { 0, 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;
}
else
{
return monthArray[month];
}
}
//+= 返回自身的引用,減少拷貝
Date& operator+=(int day)
{
//判斷是否加了負(fù)數(shù)
if (day < 0)
{
//復(fù)用
*this -= -day;
return *this;
}
_day += day;
while (_day > GetMonthDay(_year, _month));
{
_day -= GetMonthDay(_year, _month);
//進(jìn)位
_month++;
if (_month == 13)
{
_year++;
_month = 1;
}
}
return *this;
}
//-= 返回自身的引用,減少拷貝
Date& operator-=(int day)
{
//判斷是否減了一個(gè)負(fù)數(shù)
if (day < 0)
{
//復(fù)用
*this += -day;
return *this;
}
_day -= day;
while (_day <= 0)
{
--_month;
if (_month == 0)
{
--_year;
_month = 12;
}
_day += GetMonthDay(_year, _month);
}
return *this;
}
Date operator+(int day)
{
//拷貝構(gòu)造
//因?yàn)榧硬桓淖冏陨淼闹?,所以?chuàng)建臨時(shí)對象
Date tmp(*this);
//復(fù)用
tmp += day;
return tmp;
}
Date operator-(int day)
{
Date tmp(*this);
tmp -= day;
return tmp;
}
//...
};
(3)前置++與后置++重載
前置++
與后置++
都是一元運(yùn)算符,這二者的區(qū)別是:
- 前置
++
:先++
再使用,返回++
之后的數(shù) - 后置
++
:先使用再++
,返回++
之前的數(shù)
為了能在重載的時(shí)候做出區(qū)分,·C++·規(guī)定:
-
后置
++
重載時(shí)多增加一個(gè)int
類型的參數(shù),但調(diào)用時(shí)該參數(shù)不用傳遞,編譯器會自動傳遞。
class Date
{
public:
//...
//前置++
Date& operator++()
{
*this += 1;
return *this;
}
//后置++
// 注意:后置++是先使用后+1,因此需要返回+1之前的舊值,
// 故需在實(shí)現(xiàn)時(shí)需要先將this保存一份,然后給this + 1
// 而temp是臨時(shí)對象,因此只能以值的方式返回,不能返回引用
Date operator++(int)
{
Date tmp(*this);
*this += 1;
return tmp;
}
//前置--
Date& operator--()
{
*this -= 1;
return *this;
}
//后置--
Date operator--(int)
{
Date tmp(*this);
*this -= 1;
return tmp;
}
//...
};
(4)日期-日期的實(shí)現(xiàn)
日期+日期沒有意義,但是日期-日期有意義,日期-日期代表兩日期相距多少天
class Date
{
//...
int operator-(const Date& d)
{
Date max = *this;
Date min = d;
int flag = 1;
if (*this < d)
{
max = d;
min = *this;
flag = -1;
}
int n = 0;
while (min != max)
{
++min;
++n;
}
return n * flag;
}
//...
}
(5)<< 與 >>重載
Date d1(2023,5,1);
cout<<d1;
-
<<
與>>
是二元操作符,上文提到二元操作符第一個(gè)參數(shù)為左操作符,第二個(gè)參數(shù)為右操作符 - 因?yàn)轭愔谐蓡T函數(shù)第一個(gè)參數(shù)為
this
,所以左操作數(shù)就成了對象,右操作數(shù)變成了cout
,這就成了d1<<cout
,與平常使用的C++
語法習(xí)慣不符,所以我們不能將<<
與>>
寫到類的成員函數(shù)中,而是重載在類外面 - 但是類外的函數(shù)無法訪問類的私有函數(shù),所以我們將重載函數(shù)設(shè)置為友元函數(shù)來實(shí)現(xiàn)。
class Date
{
//...
//申明友元函數(shù)
friend ostream& operator<<(ostream& out, const Date& d);
friend istream& operator>>(istream& in, Date& d);
//...
}
ostream& operator<<(ostream& out, const Date& d)
{
out << d._year << "年" << d._month << "月" << d._day << "日";
return out;
}
istream& operator>>(istream& in, Date& d)
{
in >> d._year >> d._month >> d._day;
return in;
}
2.默認(rèn)成員函數(shù)——賦值運(yùn)算符重載
與之前的構(gòu)造函數(shù)與析構(gòu)函數(shù)等默認(rèn)成員函數(shù)相同,賦值運(yùn)算符重載也是類的6個(gè)默認(rèn)成員函數(shù)之一。
賦值運(yùn)算符重載具有以下特性:
- 賦值運(yùn)算符重載格式:
參數(shù)類型:const &T
,參數(shù)引用可以提高傳參效率
返回值類型:T&
,返回值引用可以提高返回的效率,有返回值目的是為了支持連續(xù)賦值,檢測是否給自己連續(xù)賦值
返回*this
:要復(fù)合連續(xù)賦值的定義 - 賦值運(yùn)算符只能重載成類的成員函數(shù)不能重載成全局函數(shù);
class Date
{
//...
Date& operator=(const Date& d)
{
if (this != &d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
return *this;
}
//...
}
二.默認(rèn)成員函數(shù)——取地址操作符重載
6個(gè)默認(rèn)成員函數(shù)只剩兩個(gè)——取地址重載與const
取地址重載。但是,這兩個(gè)函數(shù)實(shí)在沒有實(shí)現(xiàn)的必要,因?yàn)槲覀冏约簩?shí)現(xiàn)與編譯器自動實(shí)現(xiàn)出來的效果是一樣的。
class Date
{
//...
Date* operator&()
{
return this;
}
const Date* operator&()const
{
return this;
}
//...
};
#. 補(bǔ)充知識點(diǎn):const成員
將const
修飾的成員函數(shù)稱之為const
成員函數(shù),const
修飾類成員函數(shù),實(shí)際修飾的該成員函數(shù)隱含的this
指針,表明在該成員函數(shù)中不能對類的任何成員進(jìn)行修改。
例如:
class Date
{
public:
//...
void print()
{
cout << _year << "年" << _month << "月" << _day << "日" << endl;
}
private:
int _year;
int _month;
int _day;
}
void Test3()
{
Date d1(2023, 4, 1);
d1.print();
const Date d2(2022, 3, 1);
d2.print();
}
運(yùn)行結(jié)果:
這是因?yàn)?strong>權(quán)限放大了:我們不能將const Date &d2
傳遞給形參Date* this
正確寫法為:
void print() const
{
cout << _year << "年" << _month << "月" << _day << "日" << endl;
}
無法顯示的修飾隱含參數(shù)*this
,所以在函數(shù)后面加上const
修飾。這樣做適合不在成員函數(shù)內(nèi)修改成員變量的函數(shù),對無const
修飾的類同樣適用。文章來源:http://www.zghlxwxcb.cn/news/detail-438084.html
本文到此結(jié)束,碼文不易,還請多多支持哦!文章來源地址http://www.zghlxwxcb.cn/news/detail-438084.html
到了這里,關(guān)于c++類與對象(二)——賦值運(yùn)算符重載與取地址操作符重載的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!