C++ 多態性 虛函數、抽象類(一)

注意:本文中代碼均使用 Qt 開發編譯環境

虛函數是動態聯編的基礎。虛函數必須是基類的非靜態成員函數,其訪問權限可以是protected或public,在基類的類定義中定義虛函數的一般形式:

virtual returnType functionName (param...) { 
        // some codes
}

我們知道,根據類型兼容規則,可以使用派生類的對象代替基類對象。如果用基類類型的指針指向派生類對象,就可以通過這個指針來訪問該對象,問題是訪問到的只是從基類繼承來的同名成員。解決這一問題的辦法是:如果需要通過基類的指針指向派生類的對象,并訪問某個與基類同名的成員,那么首先在基類中將這個同名函數說明為虛函數。這樣,通過基類類型的指針,就可以使屬于不同派生類的不同對象產生不同的行為,從而實現了運行過程的多態。

動態聯編規定,只能通過指向基類的指針或基類對象的引用來調用虛函數,其格式為:

ptrToBaseClass->virtualFunctionName(param...);

quoteToBaseClass.virtualFunctionName(param...);

虛函數是C++多態的一種表現
  
例如:子類繼承了父類的一個函數(方法),而我們把父類的指針指向子類,則必須把父類的該函數(方法)設為virtual(虛函數)。

使用虛函數,我們可以靈活的進行動態聯編,當然是以一定的開銷為代價。

如果父類的函數(方法)根本沒有必要或者無法實現,完全要依賴子類去實現的話,可以把此函數(方法)設為:

virtual returnType functionName(params) = 0;

我們把這樣的函數(方法)稱為純虛函數。而如果一個類包含了純虛函數,我們稱此類為抽象類

虛函數示例:

#include <QCoreApplication>
#include <QDebug>

class B{
public:
    virtual void display(){ qDebug() << "B::display()"; }
};

class C: public B{
public:
    void display(){ qDebug() << "C::display()"; }
};

class D: public C{
public:
    void display(){ qDebug() << "D::display()"; }
};

void fun(B *ptr){
    ptr->display();
}

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

    B b,*p;
    C c;
    D d;

    p = &b; fun(p);
    p = &c; fun(p);
    p = &d; fun(p);

    return a.exec();
}

運行輸出:

B::display()
C::display()
D::display()

在本例子中,派生類并沒有顯式給出虛函數聲明,這時系統就會遵循以下規則來判斷派生類的一個函數成員是不是虛函數:

(1)該函數是否與基類的虛函數有相同的名稱;
(2)該函數是否與基類的虛函數有相同的參數個數及相同的對應參數類型;
(3)該函數是否與基類的虛函數有相同的返回值或者滿足類型兼容規則的指針、引用型的返回值。

如果從名稱、參數以及返回值三個方面檢查之后,派生類的函數滿足了上述條件,就會自動確定為虛函數。這時,派生類的虛函數就會覆蓋基類的虛函數。不僅如此,派生類中的虛函數還會隱藏基類中同名函數的所有其他重載形式。

當基類的構造函數調用虛函數時,不會調用派生類的虛函數。這是因為基類被構造時,對象還不是一個派生類對象。同樣,當基類被析構時,對象已經不再是一個派生類對象了。所以基類此時調用的是基類的虛函數。

只有虛函數是動態聯編的,如果派生類需要修改基類的行為(即重寫與基類函數同名的函數),就應該在基類中將相應的函數聲明為虛函數。而基類中聲明的非虛函數,通常代表那些不希望被派生類改變的功能,也是不能實現多態的。因此一般不要重寫繼承來的非虛函數(雖然語法上并沒有強行限制)。

在重寫繼承來的虛函數時,如果函數有默認形參值,千萬不要重新定義不同的值。原因是:雖然虛函數是動態聯編的,但默認形參值是靜態綁定的。也就是說通過一個指向派生類對象的基類指針,可以訪問到派生類的虛函數,但默認形參值卻只能來自基類的定義。

實現動態聯編需要三個條件:
1、 必須把動態聯編的行為定義為類的虛函數。
2、 類之間存在子類型關系,一般表現為一個類從另一個類公有派生而來。
3、 必須先使用基類指針指向子類型的對象,然后直接或者間接使用基類指針調用虛函數。

定義虛函數的限制
(1)非類的成員函數不能定義為虛函數,類的成員函數中靜態成員函數和構造函數也不能定義為虛函數,但可以將析構函數定義為虛函數。實際上,優秀的程序員常常把基類的析構函數定義為虛函數。因為,將基類的析構函數定義為虛函數后,當利用delete刪除一個指向派生類定義的對象指針時,系統會調用相應的類的析構函數。而不將析構函數定義為虛函數時,只調用基類的析構函數。

(2)只需要在聲明函數的類體中使用關鍵字“virtual”將函數聲明為虛函數,而定義函數時不需要使用關鍵字“virtual”。

(3)當將基類中的某一成員函數聲明為虛函數后,派生類中的同名函數自動成為虛函數。

(4)如果聲明了某個成員函數為虛函數,則在該類中不能出現和這個成員函數同名并且返回值、參數個數、類型都相同的非虛函數。在以該類為基類的派生類中,也不能出現這種同名函數。

虛函數聯系到多態,多態聯系到繼承。所以本文中都是在繼承層次上做文章。沒了繼承,什么都沒得談。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,182評論 6 543
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,489評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,290評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,776評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,510評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,866評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,860評論 3 447
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,036評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,585評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,331評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,536評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,058評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,754評論 3 349
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,154評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,469評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,273評論 3 399
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,505評論 2 379

推薦閱讀更多精彩內容

  • 前言 把《C++ Primer》[https://book.douban.com/subject/25708312...
    尤汐Yogy閱讀 9,532評論 1 51
  • C++虛函數 C++虛函數是多態性實現的重要方式,當某個虛函數通過指針或者引用調用時,編譯器產生的代碼直到運行時才...
    小白將閱讀 1,754評論 4 19
  • 這是知乎上c++虛函數的作用https://www.zhihu.com/question/23971699CSDN...
    吳業鵬閱讀 1,146評論 0 1
  • 很小的時候就喜歡讀書繪畫。且學習成績優異。因為是出身在比較貧困的農村。大多數人沒有條件。並不是因為學習不好,真的只...
    當假設已成立閱讀 254評論 0 0
  • 我曾期待有一段美好的愛情。曾單純的以為,愛屋及烏是愛的真諦! 直到,我遇見心動,遇見愛情的時候,以...
    陌途魂歸閱讀 167評論 0 0