C++ 多態(tài)性 運(yùn)算符重載

注意:本文中代碼均使用 Qt 開發(fā)編譯環(huán)境

面向?qū)ο蟮亩鄳B(tài)性可以分為四類:重載多態(tài)、強(qiáng)制多態(tài)昂利、包含多態(tài)和參數(shù)多態(tài),前兩種統(tǒng)稱為專用多態(tài)铁坎,而后兩種也稱為通用多態(tài)蜂奸。

多態(tài)的實(shí)現(xiàn)###

多態(tài)從實(shí)現(xiàn)的角度可以劃分為:編譯時(shí)多態(tài)和運(yùn)行時(shí)的多態(tài)。

確定操作的具體對象的過程就是綁定(binding厢呵,也叫聯(lián)編)窝撵。綁定是指計(jì)算機(jī)程序自身彼此關(guān)聯(lián)的過程傀顾,也就是把一個(gè)標(biāo)識(shí)符名和一個(gè)存儲(chǔ)地址聯(lián)系在一起的過程襟铭;就是把一條消息和一個(gè)對象的方法相結(jié)合的過程。按照綁定進(jìn)行的階段不同短曾,可分為:靜態(tài)綁定和動(dòng)態(tài)綁定寒砖,這兩種綁定過程中分別對應(yīng)著多態(tài)的兩種實(shí)現(xiàn)方式:
1.綁定工作在編譯連接階段完成的情況稱為靜態(tài)綁定。也叫早期綁定或前綁定嫉拐。
2.綁定工作在程序運(yùn)行階段完成的情況稱為動(dòng)態(tài)綁定哩都。也叫晚期綁定或后綁定。

運(yùn)算符重載###

C++ 中預(yù)定義的運(yùn)算符的操作對象只能是基本數(shù)據(jù)類型婉徘。但實(shí)際上漠嵌,對于許多用戶自定義類型(例如類),也需要類似的運(yùn)算操作盖呼。這時(shí)就必須在C++中重新定義這些運(yùn)算符儒鹿,賦予已有運(yùn)算符新的功能,使它能夠用于特定類型執(zhí)行特定的操作几晤。運(yùn)算符重載的實(shí)質(zhì)是函數(shù)重載约炎,它提供了 C++ 的可擴(kuò)展性,也是 C++ 最吸引人的特性之一。

運(yùn)算符重載是通過創(chuàng)建運(yùn)算符函數(shù)實(shí)現(xiàn)的圾浅,運(yùn)算符函數(shù)定義了重載的運(yùn)算符將要進(jìn)行的操作掠手。運(yùn)算符函數(shù)的定義與其他函數(shù)的定義類似,惟一的區(qū)別是運(yùn)算符函數(shù)的函數(shù)名是由關(guān)鍵字 operator 和其后要重載的運(yùn)算符符號(hào)構(gòu)成的狸捕。

運(yùn)算符函數(shù)定義的一般格式如下:

<返回類型說明符> operator  <運(yùn)算符符號(hào)> (<參數(shù)表>)
{
     <函數(shù)體>
}

運(yùn)算符重載時(shí)要遵循以下規(guī)則:

(1) 除了類屬關(guān)系運(yùn)算符 "." 喷鸽、成員指針運(yùn)算符 ".*" 、作用域運(yùn)算符 "::" 府寒、sizeof 運(yùn)算符和三目運(yùn)算符 "?:" 以外魁衙,C++ 中的所有運(yùn)算符都可以重載。

(2) 重載運(yùn)算符限制在 C++ 語言中已有的運(yùn)算符范圍內(nèi)的允許重載的運(yùn)算符之中株搔,不能創(chuàng)建新的運(yùn)算符剖淀。

(3) 運(yùn)算符重載實(shí)質(zhì)上是函數(shù)重載,因此編譯程序?qū)\(yùn)算符重載的選擇纤房,遵循函數(shù)重載的選擇原則纵隔。

(4) 重載之后的運(yùn)算符不能改變運(yùn)算符的優(yōu)先級和結(jié)合性,也不能改變運(yùn)算符操作數(shù)的個(gè)數(shù)及語法結(jié)構(gòu)炮姨。

(5) 運(yùn)算符重載不能改變該運(yùn)算符用于內(nèi)部類型對象的含義捌刮。它只能和用戶自定義類型的對象一起使用,或者用于用戶自定義類型的對象和內(nèi)部類型的對象混合使用時(shí)舒岸。

(6) 運(yùn)算符重載是針對新類型數(shù)據(jù)的實(shí)際需要對原有運(yùn)算符進(jìn)行的適當(dāng)?shù)母脑焐鹱鳎剌d的功能應(yīng)當(dāng)與原有功能相類似,避免沒有目的地使用重載運(yùn)算符蛾派。

示例1:

#include <QCoreApplication>
#include <QDebug>

class complex{
public:
    complex(double r=0.0, double i=0.0) : real(r), imag(i) {}
    complex operator +(complex c2);
    complex operator -(complex c2);
    QString display() const;

private:
    double real;
    double imag;
};

complex complex::operator + (complex c2){
    return complex(real + c2.real, imag + c2.imag);
}

complex complex::operator - (complex c2){
    return complex(real - c2.real, imag - c2.imag);
}

QString complex::display() const {
    return  "(" + QString::number(real) + ", " + QString::number(imag) + ")";
}

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    complex c1(5, 4), c2(2, 10), c3;

    qDebug() << "c1 = " << c1.display();
    qDebug() << "c2 = " << c2.display();

    c3 = c1 - c2;
    qDebug() << "c3 = c1 - c2 = " << c3.display();

    c3 = c1 + c2;
    qDebug() << "c3 = c1 + c2 = " << c3.display();

    return a.exec();
}

運(yùn)行結(jié)果:


運(yùn)行結(jié)果

運(yùn)算符函數(shù)重載一般有兩種形式:重載為類的成員函數(shù)和重載為類的非成員函數(shù)俄认。非成員函數(shù)通常是友元。(可以把一個(gè)運(yùn)算符作為一個(gè)非成員洪乍、非友元函數(shù)重載眯杏。但是,這樣的運(yùn)算符函數(shù)訪問類的私有和保護(hù)成員時(shí)壳澳,必須使用類的公有接口中提供的設(shè)置數(shù)據(jù)和讀取數(shù)據(jù)的函數(shù)岂贩,調(diào)用這些函數(shù)時(shí)會(huì)降低性能∠锊ǎ可以內(nèi)聯(lián)這些函數(shù)以提高性能萎津。)

成員函數(shù)運(yùn)算符###

運(yùn)算符重載為類的成員函數(shù)的一般格式為:

<函數(shù)類型> operator <運(yùn)算符> ( <參數(shù)表> ) {
     <函數(shù)體>
}    

當(dāng)運(yùn)算符重載為類的成員函數(shù)時(shí),函數(shù)的參數(shù)個(gè)數(shù)比原來的操作數(shù)要少一個(gè)(后置單目運(yùn)算符除外)抹镊,這是因?yàn)槌蓡T函數(shù)用this指針隱式地訪問了類的一個(gè)對象锉屈,它充當(dāng)了運(yùn)算符函數(shù)最左邊的操作數(shù)。因此:

(1) 雙目運(yùn)算符重載為類的成員函數(shù)時(shí)髓考,函數(shù)只顯式說明一個(gè)參數(shù)部念,該形參是運(yùn)算符的右操作數(shù)。

(2) 前置單目運(yùn)算符重載為類的成員函數(shù)時(shí),不需要顯式說明參數(shù)儡炼,即函數(shù)沒有形參妓湘。

(3) 后置單目運(yùn)算符重載為類的成員函數(shù)時(shí),函數(shù)要帶有一個(gè)整型形參乌询。

調(diào)用成員函數(shù)運(yùn)算符的格式如下:

<對象名>.operator <運(yùn)算符>(<參數(shù)>)

它等價(jià)于

<對象名><運(yùn)算符><參數(shù)>

例如:a+b等價(jià)于a.operator+(b)榜贴。一般情況下,我們采用運(yùn)算符的習(xí)慣表達(dá)方式妹田。

示例2:

#include <QCoreApplication>
#include <QDebug>

class Clock {
public:
    explicit Clock();
    Clock(int NewH=0,int NewM=0,int NewS=0);
    QString ShowTime() const;
    Clock& operator ++();   //前置單目運(yùn)算符重載
    Clock operator ++(int);  //后置單目運(yùn)算符重載

private:
    int Hour, Minute, Second;
};

Clock::Clock() : Hour(0),  Minute(0), Second(0) {
}

Clock::Clock(int NewH, int NewM, int NewS) {
    Hour = qMax(0, qMin(NewH, 23));
    Minute = qMax(0, qMin(NewM, 59));
    Second = qMax(0, qMin(NewS, 59));
}

QString Clock::ShowTime() const {
    return QString::number(Hour) + ":"
            + QString::number(Minute) + ":"
            + QString::number(Second);
}

Clock& Clock::operator ++() {
    if (++Second>=60) {
        Second = Second - 60;
        if (++Minute >= 60) {
            Minute = Minute - 60;
            Hour = (++Hour) % 24;
        }
    }
    return *this;
}

Clock Clock::operator ++(int) {
    Clock old = *this;
    ++(*this);
    return old;
}

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    Clock myClock(23,59,59);
    qDebug() << "myClock is: "  << myClock.ShowTime();
    qDebug() << "myClock++:"    << (myClock++).ShowTime();
    qDebug() << "myClock is: "  << myClock.ShowTime();
    qDebug() << "++myClock:"    << (++myClock).ShowTime();
    qDebug() << "++myClock:"    << (++myClock).ShowTime();

    return a.exec();
}

運(yùn)行結(jié)果:


運(yùn)行結(jié)果

友元函數(shù)運(yùn)算符###

運(yùn)算符重載為類的友元函數(shù)的一般格式為:

friend <函數(shù)類型> operator <運(yùn)算符> ( <參數(shù)表> ) {
     <函數(shù)體>
}

當(dāng)運(yùn)算符重載為類的友元函數(shù)時(shí)唬党,由于沒有隱含的this指針,因此操作數(shù)的個(gè)數(shù)沒有變化鬼佣,所有的操作數(shù)都必須通過函數(shù)的形參進(jìn)行傳遞驶拱,函數(shù)的參數(shù)與操作數(shù)自左至右一一對應(yīng)。

調(diào)用友元函數(shù)運(yùn)算符的格式如下:

operator <運(yùn)算符>(<參數(shù)1>,<參數(shù)2>)

它等價(jià)于

<參數(shù)1><運(yùn)算符><參數(shù)2>

例如:a+b等價(jià)于operator+(a,b)晶衷。

友元示例:

#include <QCoreApplication>
#include <QDebug>

class complex {
public:
    complex(double r=0.0,double i=0.0) : real(r), imag(i) {
    }
    friend complex operator + (complex c1, complex c2);
    friend complex operator - (complex c1, complex c2);
    QString display() const;
private:
    double real;
    double imag;
};

complex operator + (complex c1,complex c2) {
    return complex(c1.real + c2.real, c1.imag + c2.imag);
}

complex operator - (complex c1,complex c2) {
    return complex(c1.real - c2.real, c1.imag - c2.imag);
}

QString complex::display() const {
    return "(" + QString::number(real) + ","
            + QString::number(imag) +")";
}

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    complex c1(5,4), c2(2,10), c3;

    qDebug() << "c1 =" << c1.display();
    qDebug() << "c2 =" << c2.display();

    c3 = c1 - c2;
    qDebug() << "c3 = c1-c2 = " << c3.display();

    c3 = c1 + c2;
    qDebug() << "c3 = c1+c2 = " << c3.display();

    return a.exec();
}

運(yùn)行結(jié)果:


運(yùn)行結(jié)果

兩種重載形式的比較

在多數(shù)情況下蓝纲,將運(yùn)算符重載為類的成員函數(shù)和類的友元函數(shù)都是可以的。但成員函數(shù)運(yùn)算符與友元函數(shù)運(yùn)算符也具有各自的一些特點(diǎn):

(1) 一般情況下晌纫,單目運(yùn)算符最好重載為類的成員函數(shù)税迷;雙目運(yùn)算符則最好重載為類的友元函數(shù)。

(2) 以下一些雙目運(yùn)算符不能重載為類的友元函數(shù):=锹漱、()箭养、[]、->哥牍。

(3) 類型轉(zhuǎn)換函數(shù)只能定義為一個(gè)類的成員函數(shù)而不能定義為類的友元函數(shù)毕泌。

(4) 若一個(gè)運(yùn)算符的操作需要修改對象的狀態(tài),選擇重載為成員函數(shù)較好砂心。

(5) 若運(yùn)算符所需的操作數(shù)(尤其是第一個(gè)操作數(shù))希望有隱式類型轉(zhuǎn)換懈词,則只能選用友元函數(shù)蛇耀。

(6) 當(dāng)運(yùn)算符函數(shù)是一個(gè)成員函數(shù)時(shí)辩诞,最左邊的操作數(shù)(或者只有最左邊的操作數(shù))必須是運(yùn)算符類的一 個(gè)類對象(或者是對該類對象的引用)。如果左邊的操作數(shù)必須是一個(gè)不同類的對象纺涤,或者是一個(gè)內(nèi)部 類型的對象译暂,該運(yùn)算符函數(shù)必須作為一個(gè)友元函數(shù)來實(shí)現(xiàn)。

(7) 當(dāng)需要重載運(yùn)算符具有可交換性時(shí)撩炊,選擇重載為友元函數(shù)外永。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市拧咳,隨后出現(xiàn)的幾起案子伯顶,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 222,627評論 6 517
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件祭衩,死亡現(xiàn)場離奇詭異灶体,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)掐暮,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 95,180評論 3 399
  • 文/潘曉璐 我一進(jìn)店門蝎抽,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人路克,你說我怎么就攤上這事樟结。” “怎么了精算?”我有些...
    開封第一講書人閱讀 169,346評論 0 362
  • 文/不壞的土叔 我叫張陵瓢宦,是天一觀的道長。 經(jīng)常有香客問我灰羽,道長刁笙,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 60,097評論 1 300
  • 正文 為了忘掉前任谦趣,我火速辦了婚禮疲吸,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘前鹅。我一直安慰自己摘悴,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 69,100評論 6 398
  • 文/花漫 我一把揭開白布舰绘。 她就那樣靜靜地躺著蹂喻,像睡著了一般。 火紅的嫁衣襯著肌膚如雪捂寿。 梳的紋絲不亂的頭發(fā)上口四,一...
    開封第一講書人閱讀 52,696評論 1 312
  • 那天,我揣著相機(jī)與錄音秦陋,去河邊找鬼蔓彩。 笑死,一個(gè)胖子當(dāng)著我的面吹牛驳概,可吹牛的內(nèi)容都是我干的赤嚼。 我是一名探鬼主播,決...
    沈念sama閱讀 41,165評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼顺又,長吁一口氣:“原來是場噩夢啊……” “哼更卒!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起稚照,我...
    開封第一講書人閱讀 40,108評論 0 277
  • 序言:老撾萬榮一對情侶失蹤蹂空,失蹤者是張志新(化名)和其女友劉穎俯萌,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體上枕,經(jīng)...
    沈念sama閱讀 46,646評論 1 319
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡绳瘟,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,709評論 3 342
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了姿骏。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片糖声。...
    茶點(diǎn)故事閱讀 40,861評論 1 353
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖分瘦,靈堂內(nèi)的尸體忽然破棺而出蘸泻,到底是詐尸還是另有隱情,我是刑警寧澤嘲玫,帶...
    沈念sama閱讀 36,527評論 5 351
  • 正文 年R本政府宣布悦施,位于F島的核電站,受9級特大地震影響去团,放射性物質(zhì)發(fā)生泄漏抡诞。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 42,196評論 3 336
  • 文/蒙蒙 一土陪、第九天 我趴在偏房一處隱蔽的房頂上張望昼汗。 院中可真熱鬧,春花似錦鬼雀、人聲如沸顷窒。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,698評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽鞋吉。三九已至,卻和暖如春励烦,著一層夾襖步出監(jiān)牢的瞬間谓着,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,804評論 1 274
  • 我被黑心中介騙來泰國打工坛掠, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留赊锚,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 49,287評論 3 379
  • 正文 我出身青樓却音,卻偏偏與公主長得像改抡,于是被迫代替她去往敵國和親矢炼。 傳聞我的和親對象是個(gè)殘疾皇子系瓢,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,860評論 2 361

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

  • C++運(yùn)算符重載-上篇 本章內(nèi)容:1. 運(yùn)算符重載的概述2. 重載算術(shù)運(yùn)算符3. 重載按位運(yùn)算符和二元邏輯運(yùn)算符4...
    Haley_2013閱讀 2,305評論 0 51
  • 前言 把《C++ Primer》[https://book.douban.com/subject/25708312...
    尤汐Yogy閱讀 9,523評論 1 51
  • C++運(yùn)算符重載-下篇 本章內(nèi)容:1. 運(yùn)算符重載的概述2. 重載算術(shù)運(yùn)算符3. 重載按位運(yùn)算符和二元邏輯運(yùn)算符4...
    Haley_2013閱讀 1,445評論 0 49
  • 重新系統(tǒng)學(xué)習(xí)下C++;但是還是少了好多知識(shí)點(diǎn)句灌;socket夷陋;unix欠拾;stl;boost等骗绕; C++ 教程 | 菜...
    kakukeme閱讀 19,946評論 0 50
  • 童年藐窄,似乎是很久遠(yuǎn)的時(shí)光了。但每每想起酬土,依舊記憶鮮活荆忍,反復(fù)就像昨天,歡聲笑語還回蕩在耳際撤缴。 1刹枉、念念不忘故鄉(xiāng)水 ...
    蘇小矣閱讀 556評論 0 1