之前用C++寫代碼的時(shí)候,從沒仔細(xì)地思考過運(yùn)算符重載的細(xì)節(jié)矿酵。直到最近復(fù)習(xí)的時(shí)候,才發(fā)現(xiàn)這一塊知識是自己以前沒注意過的矗积。因此記錄并分享出來全肮。
友元函數(shù),friend棘捣,該函數(shù)與類的成員函數(shù)具有相同的訪問權(quán)限辜腺。朋友嘛,所以不管類里面的變量是public乍恐、protected還是private评疗,都是可以隨便訪問的。
運(yùn)算符重載茵烈,operator百匆,讓用戶自定義的類型支持運(yùn)算符操作,比如+瞧毙、-胧华、*、/宙彪、=矩动、[]、>>释漆、<<悲没、()等等。使得代碼看起來更加自然男图。比如類里面經(jīng)常使用的賦值操作示姿,就是重載了=運(yùn)算符。
那么它們倆有啥關(guān)系呢逊笆?
我們編個(gè)美好的故事栈戳,一步步的用代碼來說明。
首先难裆,王小二寫了個(gè)C++的類子檀,代表每個(gè)員工的年終獎:
#include <iostream>
class Bonus
{
public:
Bonus(int val = 0)
{
count = val;
}
~Bonus() {}
void Change(int val)
{
count += val;
}
int GetCount() const
{
return count;
}
private:
int count;
};
int main()
{
Bonus a;
a.Change(1000);
int v = a.GetCount();
std::cout << v << std::endl;
}
成員函數(shù)已經(jīng)可以實(shí)現(xiàn)了獎金的變動镊掖。但我們往往習(xí)慣直接用四則運(yùn)算,來操作數(shù)值相關(guān)的事情褂痰。
比如說亩进,這個(gè)時(shí)候,老板需要王小二的代碼可以把每個(gè)人的獎金加起來缩歪,看看到底花了多少錢归薛。于是王小二加了幾行代碼:
int main()
{
Bonus a;
a.Change(1000);
int v = a.GetCount();
std::cout << v << std::endl;
Bonus b(500);
Bonus total = a + b; // Error!
std::cout << total.GetCount() << std::endl;
}
不出意外的,a + b報(bào)錯(cuò)了匪蝙。因?yàn)榫幾g器不知道怎么去處理這個(gè)Bonus類型的加法主籍。
這個(gè)時(shí)候,我們需要重載第一個(gè)運(yùn)算符:
Bonus operator+(const Bonus& b) const
{
Bonus ret(count + b.count);
return ret;
}
通過這個(gè)運(yùn)算符的重載骗污,a + b將被翻譯成:a.operator+(b)崇猫,計(jì)算和沈条,返回一個(gè)新的Bonus實(shí)例需忿。
類似加減乘除這樣的二元運(yùn)算符,運(yùn)算符左側(cè)的a是調(diào)用方蜡歹,運(yùn)算符右側(cè)的b是作為參數(shù)被傳遞進(jìn)去屋厘。
實(shí)現(xiàn)了這個(gè)之后,完成了老板的任務(wù)月而,老板很開心:小伙子干的不錯(cuò)汗洒,給你的獎金翻番吧:
Bonus c = a * 2.0; // Error!
std::cout << c.GetCount() << std::endl;
然后,王小二發(fā)現(xiàn)又報(bào)錯(cuò)了父款。原來乘法運(yùn)算符還沒重載溢谤。趁老板沒注意,趕緊加上去:
Bonus operator*(double times) const
{
Bonus ret(count * times);
return ret;
}
阿彌陀佛憨攒,這回終于可以做乘法了世杀。
老板欣賞的看著王小二,他決定自己親自改代碼肝集,把王小二的獎金從2被變成3倍:
Bonus d = 3.0 * a;
std::cout << d.GetCount() << std::endl;
然后瞻坝,又錯(cuò)了……
難道乘法分配律在這里失效了?a * 2可以杏瞻,但3 * a不可以所刀?王小二同學(xué)一臉問號。
按照前面的描述:“二元運(yùn)算符捞挥,運(yùn)算符左側(cè)是調(diào)用方浮创,運(yùn)算符右側(cè)是作為參數(shù)被傳遞進(jìn)去∑龊” 現(xiàn)在左側(cè)是double斩披,難道王小二需要去改double類型的代碼,讓它認(rèn)識Bonus這個(gè)類型嗎……
可以使用非Bonus類成員函數(shù)的運(yùn)算符重載,王小二急中生智雏掠,非成員函數(shù)不需要依賴對象進(jìn)行調(diào)用斩祭。于是,在Bonus類的外面乡话,王小二加了這個(gè)函數(shù):
Bonus operator*(double times, const Bonus& b)
{
Bonus ret(times * b.count); // Error!
return ret;
}
但是摧玫,編譯器繼續(xù)報(bào)錯(cuò)。因?yàn)閏ount是類的私有成員绑青,非成員函數(shù)不能訪問诬像。咋辦?
這時(shí)闸婴,我們的另一個(gè)主角:友元終于可以登場了:
通過把這個(gè)非成員函數(shù)聲明為Bonus的朋友坏挠,該函數(shù)就可以訪問Bonus的內(nèi)部數(shù)據(jù)了。
在Bonus類的聲明部分(頭文件)邪乍,加上這么一行:
friend Bonus operator*(double times, const Bonus& b);
終于圓滿的編譯降狠、運(yùn)行通過了。
完整的代碼如下:
#include <iostream>
class Bonus
{
public:
Bonus(int val = 0)
{
count = val;
}
~Bonus() {}
void Change(int val)
{
count += val;
}
int GetCount() const
{
return count;
}
Bonus operator+(const Bonus& b) const
{
Bonus ret(count + b.count);
return ret;
}
Bonus operator*(double times) const
{
Bonus ret(count * times);
return ret;
}
friend Bonus operator*(double times, const Bonus& b);
private:
int count;
};
Bonus operator*(double times, const Bonus& b)
{
Bonus ret(times * b.count);
return ret;
}
int main()
{
Bonus a;
a.Change(1000);
int v = a.GetCount();
std::cout << v << std::endl;
Bonus b(500);
Bonus total = a + b;
std::cout << total.GetCount() << std::endl;
Bonus c = a * 2.0;
std::cout << c.GetCount() << std::endl;
Bonus d = 3.0 * a;
std::cout << d.GetCount() << std::endl;
}
所以庇楞,在為類Bonus重載運(yùn)算符時(shí)榜配,如果第一項(xiàng)操作數(shù)是Bonus類,那么把運(yùn)算符定義為普通的類成員函數(shù)即可吕晌;如果第一項(xiàng)操作數(shù)并非Bonus類蛋褥,我們則需要使用友元函數(shù)來翻轉(zhuǎn)操作數(shù)的順序。
至此睛驳,王小二同學(xué)終于圓滿的完成了任務(wù)烙心,并且拿到了3倍的獎金!