<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          C++ 虛函數(shù)表及多態(tài)內(nèi)部原理詳解

          共 2352字,需瀏覽 5分鐘

           ·

          2021-06-24 10:06

          置頂/星標公眾號??,硬核文章第一時間送達!

          鏈接 | https://blog.csdn.net/songguangfan/article/details/87898915


          C++ 中的虛函數(shù)的作用主要是實現(xiàn)了多態(tài)的機制。關于多態(tài),簡而言之就是用父類型別的指針指向其子類的實例,然后通過父類的指針調(diào)用實際子類的成員函數(shù)。這種技術可以讓父類的指針有“多種形態(tài)”,這是一種泛型技術。

          虛函數(shù)表

          每個含有虛函數(shù)的類都有一個虛函數(shù)表(Virtual Table)來實現(xiàn)的。簡稱為V-Table。C++的編譯器應該是保證虛函數(shù)表的指針存在于對象實例中最前面的位置(這是為了保證取到虛函數(shù)表的有最高的性能——如果有多層繼承或是多重繼承的情況下)。這意味著我們通過對象實例的地址得到這張?zhí)摵瘮?shù)表,然后就可以遍歷其中函數(shù)指針,并調(diào)用相應的函數(shù)。

          1、 每一個類都有虛函數(shù)列表。

          2、 虛表可以繼承,如果子類沒有重寫虛函數(shù),那么子類虛表中仍然會有該函數(shù)的地址,只不過這個地址指向的是基類的虛函數(shù)實現(xiàn)。如果基類3個虛函數(shù),那么基類的虛表中就有三項(虛函數(shù)地址),派生類也會有虛表,至少有三項,如果重寫了相應的虛函數(shù),那么虛表中的地址就會改變,指向自身的虛函數(shù)實現(xiàn)。如果派生類有自己的虛函數(shù),那么虛表中就會添加該項。

          3、 派生類的虛表中虛函數(shù)地址的排列順序和基類的虛表中虛函數(shù)地址排列順序相同,子類獨有的虛函數(shù)放在后面。

          當定義一個有虛函數(shù)類的對象時,對象的第一塊的內(nèi)存空間就是一個指向虛函數(shù)列表的指針。

          在這舉個例子

          假設我們有這樣的一個類:

          由于例程的操作環(huán)境是64位系統(tǒng),所以用long*強轉(zhuǎn)。其中(long*)(&b)就是虛函數(shù)表地址,(long*)*(long*)(&b)就是第一個函數(shù)地址,代碼運行結果如下:

          在程序中取出對象b的地址,根據(jù)對象的布局可以得出就是虛表的地址,根據(jù)這個地址可以把虛表的第一個內(nèi)存單元的內(nèi)容取出,然后強制轉(zhuǎn)換成一個函數(shù)指針,利用這個函數(shù)指針來訪問虛函數(shù)。又因為虛表是連續(xù)的,利用每次+1可以來訪問下一個內(nèi)存單元。

          如果對代碼不理解的話,可以看這幅圖就會懂了

          注意:虛函數(shù)表在最后會有一個結束標志,為1說明還有虛表,為0表示沒有虛表了 。(編譯器不同,結束標志可能存在差異)

          下面,將分別具體說明“無虛函數(shù)覆蓋”和“有虛函數(shù)覆蓋”時的虛函數(shù)表的情況。

          (一)無虛函數(shù)覆蓋

          沒有任何的繼承,虛函數(shù)表如下圖

          根據(jù)示意圖,編寫的代碼如下圖所示:

          和上一個程序一樣,根據(jù)取出虛表里面的地址強制轉(zhuǎn)換成函數(shù)指針,同樣,利用虛表的連續(xù)性,每次指針+1調(diào)用對應的虛函數(shù)。可以得出虛函數(shù)按照其聲明順序存放于虛函數(shù)表中的,子類自己的虛函數(shù)是排在父類虛函數(shù)之后的。運行結果如下圖


          (二)一般繼承(有虛函數(shù)覆蓋)

          如果子類中有虛函數(shù)重載了父類的虛函數(shù),會是一個什么樣子?假設,我們有下面這樣的一個繼承關系。如圖所示:

          在這個類的設計中,只覆蓋了父類的一個函數(shù):f()。那么,對于派生類的實例,其虛函數(shù)表會是下面的一個樣子:

          從表中可以看到下面幾點,

          1)覆蓋的f()函數(shù)被放到了虛表中原來父類虛函數(shù)的位置。

          2)沒有被覆蓋的函數(shù)依舊。

          這樣就會出現(xiàn)虛調(diào)用

          Base *b = new Derive();

          b->f();

          由b所指的內(nèi)存中的虛函數(shù)表的f()的位置已經(jīng)被Derive::f()函數(shù)地址所取代,于是在實際調(diào)用發(fā)生時,是Derive::f()被調(diào)用了。這就實現(xiàn)了多態(tài)。下面我們用一個示例代碼來看一下

          運行結果如下,確實如我們以上分析的那樣,由b所指的內(nèi)存中的虛函數(shù)表的f()的位置已經(jīng)被Derive::f()函數(shù)地址所取代:

          (三)多重繼承(無虛函數(shù)覆蓋)

          下面我們再看看多重繼承的情況

          對于子類實例中的虛函數(shù)表,是下面這個樣子:

          從圖上我們可以看到

          1)每個父類都有自己的虛表。

          2) 子類的成員函數(shù)被放到了第一個父類的表中。(所謂的第一個父類是按照聲明順序來判斷的)

          這樣做就是為了解決不同的父類類型的指針指向同一個子類實例,而能夠調(diào)用到實際的函數(shù)。

          下面我們根據(jù)上圖來實現(xiàn)一下

          運行結果如下:

          在這個程序中,子類有多個父類,因此從每個父類都繼承了一個虛表,因此會有3個虛表,根據(jù)代碼和運行結果會發(fā)現(xiàn),排列的順序和繼承的順序一樣,子類自己的虛函數(shù)排在第一個虛表的后面。程序中沒有改寫虛函數(shù) ,因此沒有覆蓋。同時主函數(shù)中應用的是一個二重指針,利用二維數(shù)組取每個虛函數(shù)地址。

          (四)多重繼承(有虛函數(shù)覆蓋)

          下面我們再來看看,如果發(fā)生虛函數(shù)覆蓋的情況。

          下圖中,我們在子類中覆蓋了父類的f()函數(shù)。

          子類虛函數(shù)列表如圖所示

          三個父類虛函數(shù)表中的f()的位置被替換成了子類的函數(shù)指針。這樣,我們就可以任一靜態(tài)類型的父類來指向子類,并調(diào)用子類的f()了。

          子類虛函數(shù)列表訪問代碼如下:

          程序運行結果如下所示:

          本程序中,子類重寫了f函數(shù),把所有父類里面的f函數(shù)都屏蔽了。在虛表中父類f函數(shù)的位置全部換成了子類f函數(shù)的地址。因此在輸出時父類的f函數(shù)沒有了,全部是子類f函數(shù)的輸出。


          往期推薦




          專輯 | 趣味設計模式
          專輯 | 音視頻開發(fā)
          專輯 | C++ 進階
          專輯 | 超硬核 Qt
          專輯 | 玩轉(zhuǎn) Linux
          專輯 | GitHub 開源推薦
          專輯 | 程序人生


          關注公眾「高效程序員」??一起優(yōu)秀!

          回復“1024”,送你一份程序員大禮包。
          瀏覽 66
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  国产亚洲 久一区二区草榴AV | 麻豆国产免费 | 免费一级黄色视频网站 | 色视频在线播放 | 强开小嫩苞一区二区三区图片 |