第7篇:C++重載操作符

在C ++中蛙紫,我們可以使操作符(operator)為用戶定義的類在調(diào)用層可以像一般運算表達式一樣參與運算。 這意味著C ++能夠為操作符提供數(shù)據(jù)類型的特殊含義铲敛,這種能力稱為操作符重載(operator overloading)乡恕。例如,我們可以在string之類的類中重載操作符“ +”亲善,以便僅使用+即可連接兩個字符串。

重載“+”操作符

下面我們用一個類似超市的結(jié)算小程序來作為本小節(jié)的示例
Good類接口

#ifndef GOOD_HH
#define GOOD_HH
#include <string>
class Good
{
    std::string d_name;
    double d_price;

public:
    Good();
    Good(std::string, double);
    double price() const;
    void set_price(double price);
    std::string name() const;
    bool operator<(const Good &obj) const;
    //兩個Good實例 1+Good實例2
    Good operator+(Good const &good);
};
#endif

Customer接口

#ifndef CUSTOMER_HH
#define CUSTOMER_HH
#include "../header/good.hh"
#include <iostream>
#include <map>
#include <string>

class Customer
{
    std::string d_fullname; //用戶名
    double d_pay;           //應(yīng)付金額
    double d_bonus;         //獎勵積分
    double d_count;         //購買數(shù)量
    double d_discnt;        //讓利總計
    std::map<Good, double> cart;

public:
    Customer(std::string, double);
    //購買
    void buy(const Good, double);
    //支付
    void payment();
    //結(jié)帳信息

    void display_info();

private:
    //折扣
    void discount(const Good &, double);
};
#endif

類實現(xiàn)
下面的重點就是 Good Good::operator+(Good const &obj)的實現(xiàn),“+”操作符的重載函數(shù)內(nèi)部封裝了兩個Good類中的d_name字符串的拼接操作逗柴,以及兩個Good對象的d_price的加法賦值蛹头。

Good Good::operator+(Good const &obj)
{
    this->d_price += obj.d_price * 0.95;
    this->d_name = this->d_name + "和" + obj.d_name;
}

調(diào)用代碼

int main(int argc, char const *argv[])
{
    Good a{"香蕉", 5.7};
    Good b{"奇異果", 13.4};
    Good c{"榴蓮", 13.5};
    Good d{"蘋果", 5.7};
    Good e{"奶蕉", 7.7};

    Good r = a + e;

    Customer p1{"Lisa", 800};

    p1.buy(a, 23);
    p1.buy(c, 12);
    p1.buy(d, 32);
    p1.buy(r, 45);

    p1.payment();

    p1.display_info();
    return 0;
}

程序輸出


因此,我們知道重載操作符實質(zhì)上就是重載函數(shù),函數(shù)內(nèi)部封裝了運算對象(即:對象內(nèi)部各種數(shù)據(jù)成員)的對應(yīng)運算操作戏溺。

重載operator[]

除非你是你自己實現(xiàn)類似動態(tài)數(shù)組的順序存儲結(jié)構(gòu),才需要重載索引操作符渣蜗,使用標準庫的的順序容器,重載索引操作符顯得有些畫蛇添足旷祸。

以下是有關(guān)[]重載的一些特殊情況

  1. 當我們自己實現(xiàn)的順序存儲結(jié)構(gòu),需要檢查索引越界問題耕拷,[]的重載可能很有用
  2. 我們必須在函數(shù)中通過引用返回托享,因為像“ arr [i]”之類的表達式可以用作左值骚烧。

關(guān)于重載下標運算符的具體示例可以看我這篇文章,里面說的很詳細
《C++ 數(shù)據(jù)結(jié)構(gòu)--動態(tài)順序表的實現(xiàn)》

重載operator ++

重載增量運算符(operator)和減量運算符(operator--)會帶來一個小問題:每個運算符都有兩個版本,因為它們可以用作后綴運算符(例如x)或用作前綴運算符(例如x)嫌吠。

用作后綴運算符時止潘,該值的對象作為右值掺炭,臨時const對象返回辫诅,且后遞增變量本身從視圖中消失。 用作前綴運算符時涧狮,變量會遞增炕矮,其值將作為左值返回,并且可以通過修改前綴運算符的返回值來再次更改者冤。 盡管在運算符重載時不需要這些特性肤视,但強烈建議在任何重載的增量或減量運算符中實現(xiàn)這些特性。

假設(shè)我們圍繞size_t值類型定義一個包裝器類涉枫。 這樣的類可以提供以下(部分顯示)接口:

class Customer
{
    size_t d_bonus;         //獎勵積分

public:
    Customer(std::string, size_t);
    Customer &operator++();
}

類的最后一個成員聲明前綴重載的增量運算符邢滑。 返回的左值是Customer&。 該成員很容易實現(xiàn):

Customer &Customer::operator++()
{
    ++d_bonus;
    return *this;
}

要定義后綴運算符愿汰,需要定義該運算符的重載版本困后,并期望使用(虛擬)int參數(shù)。 這可能被認為是錯誤的衬廷,或者是函數(shù)重載的可接受的應(yīng)用程序摇予。 無論您對此有何看法,都可以得出以下結(jié)論:

  • 沒有參數(shù)的重載增量和減量運算符是前綴運算符吗跋,應(yīng)返回對當前對象的引用侧戴。
  • 具有int參數(shù)的重載增量和減量運算符是后綴運算符宁昭,并且應(yīng)在使用后綴運算符的位置返回一個值,該值是對象的副本酗宋。

后綴增量運算符在Customer類的接口中聲明如下:

    //后綴增量操作符重載
    Customer operator++(int);

實現(xiàn)如下,請注意,運算符的參數(shù)并未使用积仗。 在實現(xiàn)和聲明中消除前綴和后綴運算符的歧義只是實現(xiàn)的一部分。

Customer Customer::operator++(int)
{
    Customer tmp{*this};
    ++d_bonus;
    return tmp;
}

在上面的示例中蜕猫,增加當前對象的語句提供了空位保證斥扛,因為它僅涉及對原始類型的操作。 如果初始副本構(gòu)造拋出異常丹锹,則原始對象不會被修改稀颁,如果return語句拋出異常,則對象已被安全地修改楣黍。 但是增加一個對象本身可能會引發(fā)異常匾灶。 在這種情況下,如何實現(xiàn)增量運算符租漂? 再次阶女,swap是我們最好的選擇。 當數(shù)據(jù)成員增量執(zhí)行增量操作可能拋出時哩治,以下是前綴和后綴運算符提供了有力的保證:

重載new操作符和delete操作符

當運算符new重載時秃踩,它必須定義一個void *返回類型,并且其第一個參數(shù)的類型必須為size_t业筏。 默認運算符new僅定義一個參數(shù)憔杨,但是重載版本可以定義多個參數(shù)。 第一個沒有明確指定蒜胖,但是從重載了new運算符的類的對象的大小推導(dǎo)得出消别。 在本節(jié)中,將討論重載運算符new台谢。

new和delete操作符的作用域

  • 如果使用某個類的成員函數(shù)來重載這些運算符寻狂,則意味著這些運算符僅針對該特定類才被重載
  • 如果重載是在類外完成的(即它不是類的成員函數(shù)),則只要您使用這些運算符(在類內(nèi)或類外)朋沮,都將調(diào)用重載的“ new”和“ delete”蛇券。 這是全局超載。

new運算符的函數(shù)原型

void *operator new(size_t n);

重載的new運算符接收的大小為size_t類型樊拓,該大小指定要分配的內(nèi)存字節(jié)數(shù)纠亚。 重載的new的返回類型必須為void *。重載的函數(shù)返回一個指向分配的內(nèi)存塊開頭的指針骑脱。

delete運算符的函數(shù)原型

void operator delete(void*);

該函數(shù)接收一個必須刪除的void *類型的參數(shù)菜枷。 函數(shù)不應(yīng)該返回任何東西。
注意:默認情況下叁丧,重載的new和delete運算符函數(shù)都是靜態(tài)成員啤誊。 因此岳瞭,他們無權(quán)訪問此指針。
類接口

class Good
{
    std::string d_name;
    double d_price;

public:
    Good(std::string, double);
    //重載new操作符原型
    void *operator new(size_t n);
    //重載delete操作符
    void operator delete(void *);

    void display();
};

類實現(xiàn)

void *Good::operator new(size_t n)
{
    std::cout << "重載new操作符" << std::endl;
    void *p = ::new Good();
    return p;
}

void Good::operator delete(void *p)
{
    std::cout << "重載delete操作符" << std::endl;
    free(p);
    p = NULL;
}

void Good::display()
{
    std::cout << "品名:" << d_name << std::endl;
    std::cout << "價格:" << d_price << "RMB" << std::endl;
}

調(diào)用代碼

#include "source/good.cpp"
#include <iostream>

int main(int argc, char const *argv[])
{
    Good *g = new Good("香焦", 10);
    g->display();

    delete g;
    return 0;
}
示例輸出

在上面的新重載函數(shù)中蚊锹,是通過new運算符分配了動態(tài)內(nèi)存瞳筏,但是它是全局的new運算符,否則它內(nèi)部將遞歸調(diào)用因為new的內(nèi)容將一次又一次地重載牡昆,就像下面的代碼

void *p=new Good(); //錯誤的示例

正確的示例,通過::作用域操作符,從全局調(diào)用new操作符

void *p=::new Good();

在全局作用域重載new和delete操作符

void *operator new(size_t n){
     std::cout<<"全局作用域重載new操作符"<<std::endl;
     void *p=malloc(n);
     return p;
}

void operator delete(void *p){
     std::cout<<"全局作用域重載delete操作符"<<std::endl;
      free(p);
      p=NULL;
}

int main(){
      int n=5;
      int *p=new int[5];
      
    for(int i=0;i<n;i++){
        p[i]=i;
    }

   for(size_t i=0;i<n;i++){
        std::cout<<p[i]<<std::endl;
  }

 delete p;
}

在上面的代碼中姚炕,在new操作符的重載函數(shù)中,我們無法使用::new int [5]分配內(nèi)存丢烘,因為它將以遞歸方式進行柱宦。 我們只需要使用malloc分配內(nèi)存。
輸出

全局作用域重載new操作符
0 1 2 3 4
全局作用域重載delete操作符

重載new/delete操作符的原因

  • 重載的new運算符函數(shù)可以接受參數(shù)播瞳; 因此掸刊,一個類可以具有多個重載的new運算符的能力。 這使程序員在自定義對象的內(nèi)存分配方面具有更大的靈活性赢乓。 例如:
    void  *operator new(size_t n, 
    
  • 重載的new或delete運算符還為類的對象提供了垃圾回收忧侧。
  • 可以在重載的新運算符函數(shù)中添加異常處理例程。
  • 提供了重載版本的new和delete操作符能夠做一些編譯器默認版本的new和delete不能做的自定義操作牌芋。 例如蚓炬,您可能會編寫一個自定義運算符delete,以用0覆蓋釋放的內(nèi)存躺屁,以提高應(yīng)用程序數(shù)據(jù)的安全性肯夏。
  • 可以在新函數(shù)中使用realloc函數(shù)動態(tài)重新分配內(nèi)存。
  • 重載的新運算符還使程序員能夠從程序中榨取一些額外的性能楼咳。 例如熄捍,在一個類中,為了加快新節(jié)點的分配速度母怜,維護了一個已刪除節(jié)點的列表,以便在分配新節(jié)點時可以重用它們的內(nèi)存缚柏。在這種情況下苹熏,重載的delete運算符會將節(jié)點添加到列表中 刪除的節(jié)點和重載的new運算符將從列表中分配內(nèi)存,而不是從堆中分配內(nèi)存以加速內(nèi)存分配币喧。 當刪除的節(jié)點列表為空時轨域,可以使用堆中的內(nèi)存。

operator函數(shù)和普通函數(shù)有什么區(qū)別杀餐?

  • operator函數(shù)與普通函數(shù)相同
  • 唯一的區(qū)別是干发,操作符的名稱始終是操作符operator關(guān)鍵字,后跟operator關(guān)鍵字的符號史翘,并且在使用相應(yīng)的操作符時會調(diào)用operator函數(shù)枉长。
  • 幾乎所有的操作符可以重載,但以下C++內(nèi)置的操作符號是無法重載的冀续。

重載操作符的注意事項

  • 為了使操作符重載起作用,至少一個操作數(shù)必須是用戶定義的類對象必峰。
  • 賦值操作符:編譯器會為每個類自動創(chuàng)建一個默認的賦值操作符洪唐。 默認的賦值操作符確實將右側(cè)的所有成員分配到左側(cè),并且在大多數(shù)情況下都可以正常工作(此行為與復(fù)制構(gòu)造函數(shù)相同)吼蚁。 有關(guān)更多詳細信息凭需,請參見此內(nèi)容。
  • 轉(zhuǎn)換操作符:我們還可以編寫可用于將一種類型轉(zhuǎn)換為另一種類型的轉(zhuǎn)換操作符肝匆。

操作員重載規(guī)則

  • 僅內(nèi)置操作符可以重載粒蜈。 無法創(chuàng)建新的操作符。
  • 操作符的優(yōu)先級和關(guān)聯(lián)性不能更改旗国。
  • 重載的操作符不能具有默認參數(shù)薪伏,但函數(shù)調(diào)用operator() 可以具有默認參數(shù)。
  • 不能僅對內(nèi)置類型重載操作符粗仓。 必須至少使用一個操作數(shù)定義類型
  • 必須將賦值(=)嫁怀,下標([]),函數(shù)調(diào)用(“()”)和成員選擇(->)操作符定義為成員函數(shù)
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末借浊,一起剝皮案震驚了整個濱河市塘淑,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌蚂斤,老刑警劉巖存捺,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異曙蒸,居然都是意外死亡捌治,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進店門纽窟,熙熙樓的掌柜王于貴愁眉苦臉地迎上來肖油,“玉大人,你說我怎么就攤上這事臂港∩梗” “怎么了?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵审孽,是天一觀的道長县袱。 經(jīng)常有香客問我,道長佑力,這世上最難降的妖魔是什么式散? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮打颤,結(jié)果婚禮上暴拄,老公的妹妹穿的比我還像新娘漓滔。我一直安慰自己,他們只是感情好揍移,可當我...
    茶點故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布次和。 她就那樣靜靜地躺著,像睡著了一般那伐。 火紅的嫁衣襯著肌膚如雪踏施。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天罕邀,我揣著相機與錄音畅形,去河邊找鬼。 笑死诉探,一個胖子當著我的面吹牛日熬,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播肾胯,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼竖席,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了敬肚?” 一聲冷哼從身側(cè)響起毕荐,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎艳馒,沒想到半個月后憎亚,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡弄慰,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年第美,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片陆爽。...
    茶點故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡什往,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出墓陈,到底是詐尸還是另有隱情恶守,我是刑警寧澤,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布贡必,位于F島的核電站,受9級特大地震影響庸毫,放射性物質(zhì)發(fā)生泄漏仔拟。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一飒赃、第九天 我趴在偏房一處隱蔽的房頂上張望利花。 院中可真熱鬧科侈,春花似錦、人聲如沸炒事。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽挠乳。三九已至权薯,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間睡扬,已是汗流浹背盟蚣。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留卖怜,地道東北人屎开。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像马靠,于是被迫代替她去往敵國和親奄抽。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,786評論 2 345