c++多態(tài)

簡介
多態(tài)可以分為靜態(tài)和動(dòng)態(tài)倘感。
靜態(tài)多態(tài)是指當(dāng)調(diào)用名字相同的函數(shù)時(shí)放坏,編譯器在編譯期就能根據(jù)函數(shù)簽名的不同推導(dǎo)出所調(diào)用的函數(shù)。
注釋:函數(shù)簽名包括了函數(shù)名侠仇,參數(shù)列表轻姿,類名犁珠,命名空間逻炊,const關(guān)鍵字;
其實(shí)現(xiàn)也稱為函數(shù)重載(overload)犁享,將類似功能的函數(shù)統(tǒng)一命名余素,增強(qiáng)代碼的可讀性。

動(dòng)態(tài)多態(tài)一般體現(xiàn)在基類的指針可以指向一個(gè)派生類的對象炊昆,當(dāng)該指針在運(yùn)行期調(diào)用某一個(gè)虛函數(shù)時(shí)桨吊,根據(jù)所指向的對象類型的不同而調(diào)用相應(yīng)的虛函數(shù)(《c++ primier》 pp.536 “被調(diào)用的函數(shù)是與綁定到指針或引用上的對象的動(dòng)態(tài)類型相匹配的那一個(gè)”)。
當(dāng)利用設(shè)計(jì)模式來解決問題時(shí)凤巨,會(huì)利用動(dòng)態(tài)多態(tài)來實(shí)現(xiàn)视乐,如達(dá)到“對擴(kuò)展開放,對修改封閉”的目標(biāo)敢茁。

下面將主要介紹動(dòng)態(tài)多態(tài)的幾個(gè)方面:虛函數(shù)表佑淀,虛函數(shù)表指針,動(dòng)態(tài)綁定彰檬。

虛函數(shù)表指針和虛函數(shù)表
多態(tài)的實(shí)現(xiàn)是基于虛函數(shù)表指針和虛函數(shù)表伸刃。
舉例來說:
基類有兩個(gè)虛函數(shù)vf1和vf2谎砾,在編譯時(shí)編譯器會(huì)將這兩個(gè)虛函數(shù)的地址放在基類的虛函數(shù)表(vtbl)內(nèi),供該類型的對象公用捧颅。然后編譯器給基類增加一個(gè)成員變量vptr景图,即虛函數(shù)表指針,然后在基類的構(gòu)造函數(shù)中增加一句賦值的命令碉哑,當(dāng)實(shí)例化對象時(shí)挚币,通過調(diào)用構(gòu)造函數(shù)令vptr指向vtbl。
派生類由于繼承自該基類扣典,編譯器判斷基類有虛函數(shù)忘晤,那么派生類也會(huì)有一個(gè)vptr成員變量指向派生類自己的vtbl。如果派生類沒有重寫基類的虛函數(shù)激捏,則其vtbl中的虛函數(shù)地址同基類的vtbl中虛函數(shù)的地址设塔。
當(dāng)派生類重寫了某個(gè)虛函數(shù)時(shí),派生類的vtbl中相應(yīng)的函數(shù)地址就會(huì)更新為所重寫的虛函數(shù)的地址远舅。
另外闰蛔,某個(gè)類的虛函數(shù)表是該類所共有的,因此會(huì)放在數(shù)據(jù)段图柏,而類對象的虛函數(shù)表指針作為成員變量存在序六。
上述文字可以用下圖表示(來自侯捷老師的視頻):

image.png

另外,如果在派生類里面又定義了一個(gè)虛函數(shù)vf3蚤吹,而vf3不在基類中例诀,那么指向基類的指針并不會(huì)調(diào)用vf3,因?yàn)槠洳辉诨惖睦^承體系中裁着。

動(dòng)態(tài)綁定
要理解動(dòng)態(tài)綁定繁涂,自然要理解靜態(tài)綁定。
靜態(tài)綁定是指編譯器在編譯階段就能確定函數(shù)的地址二驰,因此函數(shù)調(diào)用的匯編代碼是call func_address(如圖中的14行扔罪,CALL直接綁定了Base的print()函數(shù))。

image.png

而由于要實(shí)現(xiàn)多態(tài)桶雀,那么在編譯階段矿酵,編譯器是不知道所調(diào)用的函數(shù)的地址的,那么需要通過虛函數(shù)表指針和虛函數(shù)表來確定函數(shù)地址(如上圖的21-27行矗积,CALL的地址是經(jīng)過偏移的)全肮,這個(gè)過程就是動(dòng)態(tài)綁定。
動(dòng)態(tài)綁定需要滿足三個(gè)條件:

  1. 通過指針調(diào)用函數(shù)
    Base* p = new Derived();
  2. 指針指向的對象必須支持向上轉(zhuǎn)型(up_casting)棘捣,即指針是基類的類型辜腺,指向派生類的對象
  3. 所調(diào)用的函數(shù)是虛函數(shù)
    p->vfprint()
    此時(shí),編譯后的匯編代碼不再指定具體的函數(shù)地址,而是先指向派生類的vptr哪自,然后根據(jù)函數(shù)名來讀取vtbl中對應(yīng)的函數(shù)地址
    可以將虛函數(shù)調(diào)用過程用代碼簡化表示為
(*p->vptr[n])(p); 
(*(p->vptr)[n])(p);

即p指向了派生類對象的虛函數(shù)表丰包,然后通過定位找到對應(yīng)的虛函數(shù)。注意括號(hào)內(nèi)的p相當(dāng)于傳入函數(shù)的this壤巷。
根據(jù)測試邑彪,虛函數(shù)的地址是有序的,可以從vfprint1()和vfprint2()的匯編代碼看出胧华,調(diào)用vfprint2()時(shí)地址add了8個(gè)字節(jié)(這里用的64位)


image.png

應(yīng)用場景
假設(shè)我們要畫不同的形狀寄症,可以將基類的draw設(shè)置為純虛函數(shù)矩动,然后派生類去各自實(shí)現(xiàn)。

class Shape
{
public:
    Shape() = default;
    virtual ~Shape() = default;

public:
    virtual void draw() = 0;
};

class Rectangle : public Shape
{
public:
    Rectangle() = default;
    virtual ~Rectangle() = default;

public:
    virtual void draw() override { std::cout << "draw a rectangle" << std::endl; };
};

class Circle : public Shape
{
public:
    Circle() = default;
    virtual ~Circle() = default;

public:
    virtual void draw() override { std::cout << "draw a circle" << std::endl; };
};

除了矩形和圓形之外篮迎,我們可能會(huì)在后續(xù)增加各種各樣的形狀,同時(shí)我們絕對不想改變畫出所有圖形的代碼示姿。那么在調(diào)用時(shí)可以寫成下面甜橱,這樣for循環(huán)中的代碼是不會(huì)變的,只要將不同的圖形加入到容器中就行岂傲。


int main()
{
    std::vector<Shape *> shapes;
    shapes.push_back(new Rectangle);
    shapes.push_back(new Circle);

    for (const auto &s : shapes)
    {
        s->draw();
        delete s;
    }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末子檀,一起剝皮案震驚了整個(gè)濱河市镊掖,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌褂痰,老刑警劉巖亩进,帶你破解...
    沈念sama閱讀 219,039評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異镐侯,居然都是意外死亡驶冒,警方通過查閱死者的電腦和手機(jī)韵卤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來沈条,“玉大人,你說我怎么就攤上這事√樯眨” “怎么了汗洒?”我有些...
    開封第一講書人閱讀 165,417評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長溢谤。 經(jīng)常有香客問我,道長阀参,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,868評論 1 295
  • 正文 為了忘掉前任蛛壳,我火速辦了婚禮所刀,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘赫模。我一直安慰自己,他們只是感情好瀑罗,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,892評論 6 392
  • 文/花漫 我一把揭開白布雏掠。 她就那樣靜靜地躺著,像睡著了一般乡话。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上诬像,一...
    開封第一講書人閱讀 51,692評論 1 305
  • 那天闸婴,我揣著相機(jī)與錄音,去河邊找鬼邪乍。 笑死对竣,一個(gè)胖子當(dāng)著我的面吹牛榜配,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播蛋褥,決...
    沈念sama閱讀 40,416評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼壁拉,長吁一口氣:“原來是場噩夢啊……” “哼谬俄!你這毒婦竟也來了弃理?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,326評論 0 276
  • 序言:老撾萬榮一對情侶失蹤钥勋,失蹤者是張志新(化名)和其女友劉穎辆苔,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體驻啤,經(jīng)...
    沈念sama閱讀 45,782評論 1 316
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,957評論 3 337
  • 正文 我和宋清朗相戀三年赊瞬,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了贼涩。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,102評論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡谤绳,死狀恐怖袒哥,靈堂內(nèi)的尸體忽然破棺而出缩筛,到底是詐尸還是另有隱情统诺,我是刑警寧澤,帶...
    沈念sama閱讀 35,790評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響豪硅,放射性物質(zhì)發(fā)生泄漏挺物。R本人自食惡果不足惜懒浮,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,442評論 3 331
  • 文/蒙蒙 一砚著、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧稽穆,春花似錦赶撰、人聲如沸舌镶。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽否灾。三九已至鸣奔,卻和暖如春坟冲,著一層夾襖步出監(jiān)牢的瞬間溃蔫,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評論 1 272
  • 我被黑心中介騙來泰國打工私痹, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留统刮,地道東北人紊遵。 一個(gè)月前我還...
    沈念sama閱讀 48,332評論 3 373
  • 正文 我出身青樓暗膜,卻偏偏與公主長得像,于是被迫代替她去往敵國和親学搜。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,044評論 2 355

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

  • 一聚磺、首先我們先來學(xué)習(xí)下:《Effect C++》學(xué)習(xí)------條款05:了解C++默默編寫并調(diào)用哪些函數(shù)炬丸。如果你...
    Nrocinu閱讀 184評論 0 0
  • 什么是多態(tài)性? 多態(tài):相同對象收到不同消息或不同對象收到相同消息時(shí)產(chǎn)生不同的動(dòng)作稠炬。C++支持兩種多態(tài)性:編譯時(shí)多態(tài)...
    showaichuan閱讀 586評論 0 3
  • 14.1 多態(tài)的基本概念 在C++程序中,程序的每一個(gè)函數(shù)在內(nèi)存中會(huì)被分配一段存儲(chǔ)空間暮屡,而被分配的存儲(chǔ)空間的起始地...
    飛揚(yáng)code閱讀 137評論 0 3
  • 多態(tài)原理 當(dāng)類存在虛函數(shù)時(shí)闽坡,編譯器會(huì)為該類維護(hù)一個(gè)表,這個(gè)表就是虛函數(shù)表(vtbl)疾嗅,里面存放了該類虛函數(shù)的函數(shù)指...
    第八區(qū)閱讀 168評論 0 0
  • 什么是多態(tài) 多態(tài)是指具有不同功能的函數(shù)可以用同一個(gè)函數(shù)名,這樣就可以用一個(gè)函數(shù)名調(diào)用不同內(nèi)容的函數(shù) 多態(tài)分為兩類:...
    殺破魂閱讀 378評論 0 0