c++多態(tài)

什么是多態(tài)性操禀?

多態(tài):相同對象收到不同消息或不同對象收到相同消息時產(chǎn)生不同的動作隧枫。C++支持兩種多態(tài)性:編譯時多態(tài)性纬乍,運行時多態(tài)性。

  • 編譯時多態(tài)性:通過重載函數(shù)實現(xiàn)
  • 運行時多態(tài)性:通過虛函數(shù)實現(xiàn)锁孟。

早綁定 vs 晚綁定

多態(tài)與非多態(tài)的實質(zhì)區(qū)別就是函數(shù)地址是早綁定(靜態(tài)多態(tài))還是晚綁定(動態(tài)多態(tài))彬祖。如果函數(shù)的調(diào)用,在編譯器編譯期間就可以確定函數(shù)的調(diào)用地址品抽,并生產(chǎn)代碼涧至,是靜態(tài)的,就是說地址是早綁定的桑包。而如果函數(shù)調(diào)用的地址不能在編譯器期間確定,需要在運行時才確定纺非,這就屬于晚綁定哑了。

當(dāng)類之間存在層次結(jié)構(gòu)赘方,并且類之間是通過繼承關(guān)聯(lián)時,就會用到多態(tài)弱左。
C++ 多態(tài)意味著調(diào)用成員函數(shù)時窄陡,會根據(jù)調(diào)用函數(shù)的對象的類型來執(zhí)行不同的函數(shù)。簡單概括為“一個接口拆火,多個方法”跳夭。
對于同一條命名,不同對象接到后所做出的動作是不同的们镜,這就叫多態(tài)币叹。

#include<iostream>
#include<stdlib.h>
#include "Circle.h"
#include "Rect.h"
using namespace std;

int main()
{
    Shape *shape1 = new Rect(3, 6);
    Shape *shape2 = new Circle(5);
    shape1->calcArea();
    shape2->calcArea();
    delete shape1;
    shape1 = NULL;
    delete shape2;
    shape2 = NULL;
    system("pause");
    return 0;
}

普通虛函數(shù) vs 虛析構(gòu)函數(shù)

普通虛函數(shù):在類的成員函數(shù)前加virtual關(guān)鍵字,并在派生類中重新定義的成員函數(shù)模狭,其作用是:當(dāng)父類和子類定義有相同的成員函數(shù)颈抚,用父類的指針指向子類的對象時能夠調(diào)用子類的該成員函數(shù)。因為若不加virtual關(guān)鍵字嚼鹉,那么調(diào)用的就是父類中與之同名的成員函數(shù)贩汉。
(與之區(qū)別的是隱藏,隱藏發(fā)生在子類的對象調(diào)用子類中的成員函數(shù)時而隱藏掉其父類中同名的成員函數(shù)锚赤。)
虛析構(gòu)函數(shù):在析構(gòu)函數(shù)前加virtual關(guān)鍵字匹舞,其作用是:用父類的指針指向子類的對象并在子類中開辟一段內(nèi)存時,在銷毀內(nèi)存時由于delete后面跟的是父類的對象线脚,故只調(diào)用父類的析構(gòu)函數(shù)而不調(diào)用子類的析構(gòu)函數(shù)赐稽,從而使得子類中的內(nèi)存無法釋放。而虛析構(gòu)函數(shù)就解決了這種內(nèi)存泄漏問題酒贬。

#ifndef SHAPE_H
#define SHAPE_H
class Shape
{
public:
    Shape();
    virtual ~Shape();//虛析構(gòu)函數(shù)
    virtual double calcArea();//加virtual關(guān)鍵字又憨,虛函數(shù)
};
#endif 

多態(tài)中容易出現(xiàn)的一個問題就是:內(nèi)存泄漏

若構(gòu)造函數(shù)里沒有從堆中申請內(nèi)存,那么也就不需要在析構(gòu)函數(shù)中進行釋放內(nèi)存操作锭吨,也就是說若構(gòu)造函數(shù)什么也不做(只是打印刷存在感)的話蠢莺,調(diào)用與不調(diào)用都一樣

class Circle :public Shape
{
public:
    Circle(double r );
    ~Circle();
    virtual double calcArea();
protected:
    double m_dR;
    Coordinate m_pCenter;//在Circle類中定義一個指向圓心坐標的指針
};

在Circle類中定義一個指向圓心坐標的指針,則需要在Circle類的構(gòu)造函數(shù)中申請內(nèi)存零如,并且在析構(gòu)函數(shù)中釋放內(nèi)存躏将,從而保證內(nèi)存不泄漏。
當(dāng)用父類指針指向子類對象并對操作子類對象的虛函數(shù)時考蕾,是沒問題的祸憋,但想借助父類指針去銷毀子類對象的內(nèi)存時就會出現(xiàn)問題,因為delete后面如果跟的是父類的指針肖卧,則只會調(diào)用父類的析構(gòu)函數(shù)蚯窥,若跟的是子類的指針,則既調(diào)用子類的析構(gòu)函數(shù)也調(diào)用父類的析構(gòu)函數(shù)。
而對于多態(tài)問題拦赠,就是用父類的指針去指向子類對象巍沙,并對子類對象進行操作,但釋放的是父類的指針荷鼠,此時并不調(diào)用子類的析構(gòu)函數(shù)句携,也就無法釋放子類中申請的內(nèi)存,造成內(nèi)存泄漏允乐。

為解決此問題就要用到虛析構(gòu)函數(shù)
即用virtual去修飾析構(gòu)函數(shù)(注意是在父類的析構(gòu)函數(shù)前加virtual矮嫉,子類析構(gòu)函數(shù)可加可不加,不加時系統(tǒng)會自動加上牍疏,但推薦都加上蠢笋,以防子類被其他類繼承變成新的父類)

class Shape
{
public:
    Shape();
    virtual ~Shape();//虛析構(gòu)函數(shù)
    virtual double calcArea();
};
Circle::Circle(double r)
{
    m_pCenter = new Coordinate(x, y);
    m_dR = r;
    cout << "Circle()" << endl;
}
Circle::~Circle()
{
    delete m_pCenter;
    m_pCenter = NULL;
    cout << "~Circle()" << endl;
}

virtual在修飾函數(shù)時的一些限制:

  • 不能修飾普通函數(shù)(全局函數(shù)),也即該函數(shù)必須是某個類的成員函數(shù)麸澜,否則編譯出錯
  • 不能修飾靜態(tài)函數(shù)挺尿,
  • 不能修飾內(nèi)聯(lián)函數(shù),若virtual修飾inline炊邦,則系統(tǒng)會忽略掉inline關(guān)鍵字编矾,使內(nèi)聯(lián)函數(shù)變成虛函數(shù),
  • 不能修飾構(gòu)造函數(shù)

函數(shù)指針

函數(shù)的本質(zhì)就是一段二進制的代碼寫進內(nèi)存中馁害≌危可以通過一個指針指向這段代碼的開頭,計算機就會從開頭一直執(zhí)行下去碘菜,直到結(jié)尾凹蜈。
先定義一個Shape類

class Shape
{
public:
    virtual double calcArea()
    {
        return  0;
    }
protected:
    int m_iEdge;
};

再定義一個Circle子類,其中并沒有定義計算面積的函數(shù),而是利用Shape中的虛函數(shù)來計算面積忍啸。那么此時虛函數(shù)是如何來實現(xiàn)的呢仰坦?為了弄懂這個問題,先看一個概念:虛函數(shù)表

class Circle :public Shape
{
public:
    Circle(double r);
private :
    double m_dR;
};

虛函數(shù)表

C++中的虛函數(shù)的作用主要是實現(xiàn)了多態(tài)的機制计雌。關(guān)于多態(tài)悄晃,簡而言之就是用父類型別的指針指向其子類的實例,然后通過父類的指針調(diào)用實際子類的成員函數(shù)凿滤。這種技術(shù)可以讓父類的指針有“多種形態(tài)”妈橄,這是一種泛型技術(shù)。所謂泛型技術(shù)翁脆,說白了就是試圖使用不變的代碼來實現(xiàn)可變的算法眷蚓。比如:模板技術(shù),RTTI技術(shù)反番,虛函數(shù)技術(shù)等沙热。

覆蓋 vs 隱藏 vs 重載

  • 隱藏:當(dāng)父類與子類出現(xiàn)同名的函數(shù)叉钥,實例化子類對象,并去調(diào)用該函數(shù)時只會調(diào)用子類中定義的函數(shù)而不會去調(diào)用父類中的同名函數(shù)篙贸,這就叫函數(shù)的隱藏(即指派生類的函數(shù)屏蔽了與其同名的基類函數(shù))沼侣。當(dāng)然,若一定要調(diào)用父類中的同名函數(shù)只需要在函數(shù)名前加上父類名就可以了歉秫。
  • 覆蓋:C++多態(tài)性是通過虛函數(shù)來實現(xiàn)的,虛函數(shù)允許子類重新定義成員函數(shù)养铸,而子類重新定義父類的做法稱為覆蓋(override)雁芙,或者稱為重寫。重寫的話可以有兩種钞螟,直接重寫成員函數(shù)(比如隱藏就是這種情況)和重寫虛函數(shù)(虛函數(shù)一般定義在父類中兔甘,子類中virtual關(guān)鍵字可加可不加,一般加上為好鳞滨,以防子類被其他類繼承)洞焙,只有重寫了虛函數(shù)的才能算作是體現(xiàn)了C++多態(tài)性。

如何區(qū)分覆蓋和隱藏:
如果基類中的函數(shù)和派生類中的兩個名字一樣的函數(shù)f
滿足下面的兩個條件
(a)在基類中函數(shù)聲明的時候有virtual關(guān)鍵字
(b)基類中的函數(shù)和派生類中的函數(shù)一模一樣拯啦,函數(shù)名澡匪,參數(shù),返回類型都一樣褒链。
那么這就是叫做覆蓋(override)唁情,這也就是虛函數(shù),多態(tài)的性質(zhì)
那么其他的情況呢甫匹?甸鸟?只要名字一樣,不滿足上面覆蓋的條件兵迅,就是隱藏了抢韭。

  • 重載:重載則是同一個域中允許有多個同名的函數(shù),而這些函數(shù)的參數(shù)列表不同恍箭,允許參數(shù)個數(shù)不同刻恭,參數(shù)類型不同,或者兩者都不同季惯。編譯器會根據(jù)這些函數(shù)的不同列表吠各,將同名的函數(shù)的名稱做修飾,從而生成一些不同名稱的預(yù)處理函數(shù)勉抓,來實現(xiàn)同名函數(shù)調(diào)用時的重載問題贾漏。但這并沒有體現(xiàn)多態(tài)性。

所以藕筋,相同的函數(shù)名的函數(shù)纵散,在基類和派生類中的關(guān)系只能是覆蓋或者隱藏。

RTTI

運行時類型識別(Run-Time Type Identification),它提供了運行時確定對象類型的方法伍掀。
其中有兩個重要的運算符:typeiddynamic_cast

  • typeid的name函數(shù)用于查看對象類型
    typeid(*pHuman) == typeid(Japanese)
    typeid注意事項:
    1 返回一個type-info對象的引用
    2 若想通過基類指針獲得派生類的數(shù)據(jù)類型掰茶,基類必須帶有虛函數(shù)
    3 只能獲取對象的實際類型
  • dynamic_cast 常用于從多態(tài)編程基類指針向派生類指針的向下類型轉(zhuǎn)換。它有兩個參數(shù):一個是類型名蜜笤;另一個是多態(tài)對象的指針或引用濒蒋。其功能是在運行時將對象強制轉(zhuǎn)換為目標類型并返回布爾型結(jié)果。(即編譯器無法驗證是否發(fā)生正確的轉(zhuǎn)換)
    dynamic_cast<Japanese*>(pHuman)
    dynamic_cast注意事項:
    1 轉(zhuǎn)換的類型只能是指針或引用把兔,而不能是對象本身
    2 要轉(zhuǎn)換的類型中必須包含虛函數(shù)
    3 轉(zhuǎn)換成功返回子類地址沪伙,失敗返回NULL。

純虛函數(shù)

純虛函數(shù):沒有函數(shù)體县好,且在虛函數(shù)名后面加=0
抽象類:包含純虛函數(shù)的類,由于抽象類包含了沒有定義的純虛函數(shù)围橡,所以不能定義抽象類的對象。
接口類:僅包含純虛函數(shù)的類缕贡。即無數(shù)據(jù)成員只有成員函數(shù)且成員函數(shù)均為純虛函數(shù)翁授。

接口類更多的表達的是一直能力或協(xié)議。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末晾咪,一起剝皮案震驚了整個濱河市收擦,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌禀酱,老刑警劉巖炬守,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異剂跟,居然都是意外死亡减途,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進店門曹洽,熙熙樓的掌柜王于貴愁眉苦臉地迎上來鳍置,“玉大人,你說我怎么就攤上這事送淆∷安” “怎么了?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵偷崩,是天一觀的道長辟拷。 經(jīng)常有香客問我,道長阐斜,這世上最難降的妖魔是什么衫冻? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮谒出,結(jié)果婚禮上隅俘,老公的妹妹穿的比我還像新娘邻奠。我一直安慰自己,他們只是感情好为居,可當(dāng)我...
    茶點故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布碌宴。 她就那樣靜靜地躺著,像睡著了一般蒙畴。 火紅的嫁衣襯著肌膚如雪贰镣。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天膳凝,我揣著相機與錄音八孝,去河邊找鬼。 笑死鸠项,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的子姜。 我是一名探鬼主播祟绊,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼哥捕!你這毒婦竟也來了牧抽?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤遥赚,失蹤者是張志新(化名)和其女友劉穎扬舒,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體凫佛,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡讲坎,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了愧薛。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片晨炕。...
    茶點故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖毫炉,靈堂內(nèi)的尸體忽然破棺而出瓮栗,到底是詐尸還是另有隱情,我是刑警寧澤瞄勾,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布费奸,位于F島的核電站,受9級特大地震影響进陡,放射性物質(zhì)發(fā)生泄漏愿阐。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一四濒、第九天 我趴在偏房一處隱蔽的房頂上張望换况。 院中可真熱鬧职辨,春花似錦、人聲如沸戈二。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽觉吭。三九已至腾供,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間鲜滩,已是汗流浹背伴鳖。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留徙硅,地道東北人榜聂。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓,卻偏偏與公主長得像嗓蘑,于是被迫代替她去往敵國和親须肆。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,979評論 2 355

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

  • 注意:本文中代碼均使用 Qt 開發(fā)編譯環(huán)境 虛函數(shù)是動態(tài)聯(lián)編的基礎(chǔ)。虛函數(shù)必須是基類的非靜態(tài)成員函數(shù)泄隔,其訪問權(quán)限可...
    趙者也閱讀 1,900評論 0 2
  • 多態(tài)是面向?qū)ο蟮某绦蛟O(shè)計的關(guān)鍵技術(shù)拒贱。多態(tài):調(diào)用同一個函數(shù)名,可以根據(jù)需要但實現(xiàn)不同的功能佛嬉。多態(tài)體現(xiàn)在兩個方面逻澳,我們...
    隨波逐流007閱讀 559評論 0 0
  • 本文轉(zhuǎn)載自:(做了一些改動)http://www.cnblogs.com/feixiang927/p/504856...
    安然_fc00閱讀 5,996評論 1 6
  • 繼承和多態(tài) 1. 繼承的優(yōu)缺點 優(yōu)點:(1)子類可以靈活地改變父類中的已有方法;(2)能夠最大限度的實現(xiàn)代碼重用暖呕。...
    MinoyJet閱讀 627評論 0 0
  • 如何通俗易懂地解釋「協(xié)方差」與「相關(guān)系數(shù)」的概念? - 知乎
    crazydane閱讀 177評論 0 0