C++ 友元與運(yùn)算符重載那些事

之前用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倍的獎金!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末乏沸,一起剝皮案震驚了整個(gè)濱河市淫茵,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌屎蜓,老刑警劉巖痘昌,帶你破解...
    沈念sama閱讀 217,185評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異炬转,居然都是意外死亡辆苔,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評論 3 393
  • 文/潘曉璐 我一進(jìn)店門扼劈,熙熙樓的掌柜王于貴愁眉苦臉地迎上來驻啤,“玉大人,你說我怎么就攤上這事荐吵∑锶撸” “怎么了赊瞬?”我有些...
    開封第一講書人閱讀 163,524評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長贼涩。 經(jīng)常有香客問我巧涧,道長,這世上最難降的妖魔是什么遥倦? 我笑而不...
    開封第一講書人閱讀 58,339評論 1 293
  • 正文 為了忘掉前任谤绳,我火速辦了婚禮,結(jié)果婚禮上袒哥,老公的妹妹穿的比我還像新娘缩筛。我一直安慰自己,他們只是感情好堡称,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,387評論 6 391
  • 文/花漫 我一把揭開白布瞎抛。 她就那樣靜靜地躺著,像睡著了一般却紧。 火紅的嫁衣襯著肌膚如雪桐臊。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,287評論 1 301
  • 那天啄寡,我揣著相機(jī)與錄音豪硅,去河邊找鬼。 笑死挺物,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的飘弧。 我是一名探鬼主播识藤,決...
    沈念sama閱讀 40,130評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼次伶!你這毒婦竟也來了痴昧?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,985評論 0 275
  • 序言:老撾萬榮一對情侶失蹤冠王,失蹤者是張志新(化名)和其女友劉穎赶撰,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體柱彻,經(jīng)...
    沈念sama閱讀 45,420評論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡豪娜,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,617評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了哟楷。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片瘤载。...
    茶點(diǎn)故事閱讀 39,779評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖卖擅,靈堂內(nèi)的尸體忽然破棺而出鸣奔,到底是詐尸還是另有隱情墨技,我是刑警寧澤,帶...
    沈念sama閱讀 35,477評論 5 345
  • 正文 年R本政府宣布挎狸,位于F島的核電站扣汪,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏锨匆。R本人自食惡果不足惜私痹,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,088評論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望统刮。 院中可真熱鬧紊遵,春花似錦、人聲如沸侥蒙。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽鞭衩。三九已至学搜,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間论衍,已是汗流浹背瑞佩。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留坯台,地道東北人炬丸。 一個(gè)月前我還...
    沈念sama閱讀 47,876評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像蜒蕾,于是被迫代替她去往敵國和親稠炬。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,700評論 2 354

推薦閱讀更多精彩內(nèi)容