C++虛函數表學習心得之由類執行個體地址到虛函數表再到虛函數地址中各種位址解析

來源:互聯網
上載者:User

        C++虛函數是繼承多態的核心,常規文法相信很多朋友都很熟悉了,這裡有一篇牛人關於C++虛函數表很好的部落格:http://blog.csdn.net/haoel/article/details/1948051/

我這裡只是在此基礎上談談各種地址與指標轉換的心得。

         大家都知道虛函數表在記憶體中存在的地址位於類執行個體首地址中,樓主這裡的編程環境是WIn7 64bit,VS2010,廢話少說直接看代碼吧。

class Base{public:virtual void f(){cout<<"base f()"<<endl;}virtual void g(){cout<<"base g()"<<endl;}};class Derived:public Base{public:void f(){cout<<"derived f()"<<endl;}void g(){cout<<"derived g()"<<endl;}};

         這裡有一個十分簡單的繼承關係:

 

        下面就來看看具體各種地址的情況。

        再次假設有一個Derived的執行個體d,下滿看看這張圖

          這是一張與執行個體d的虛函數表有關的記憶體模型,途中方框中表示記憶體位址中具體的值,右邊的橫線表示所在的記憶體位址,下面我們就以d為例來進行填空哈。

          首先填寫d首地址的值及內容的值,很簡單,相信學過指標的都會,d首地址是&d,d的首地址中內容是*(int*)&d,由於我機器(大部分機器)指標是4位元組,所以要進行強制轉換,將其轉換成指標好方便擷取其內容。

          大家都知道存在虛函數的類執行個體首地址內容指向其對應的虛函數表,那麼就要先將這個類執行個體首地址中的內容轉換成指標,從而得到指向虛函數表的指標,那麼我們就可以填寫虛函數表那兩個空了,虛函數表首地址:(int*)*(int*)&d,虛函數表首地址中內容的值:*(int*)*(int*)&d。

          下面看看錶中的函數,大家都知道,一維數組第一個元素的地址和數組首地址是一樣的,那麼簡而易之,虛函數表中第一個虛函數的地址和虛函數表的地址一致,虛函數表中函數的順序與具體類中虛函式宣告的順序一致,下面來填寫虛函數表第一項的空白,虛函數第一表項(Derived::f())所在記憶體位址的值:(int*)*(int*)&d,虛函數第一表項(Derived::f())所在記憶體位址其內容的值:*(int*)*(int*)&d,其實這兩個與虛函數的地址和地址中內容的值是一樣的,其內容的值是函數具體的代碼所在的記憶體的地址。

          再來填寫第二個表項,也就是虛函數Derived::g()的情況,由於兩個函數相鄰,只差一個指標的大小,也就是4位元組,因此前面的函數f()的指標+1就可以得到g()的地址:

虛函數第二表項(Derived::g())所在記憶體位址的值:(int*)*(int*)&d+1,虛函數第二表項(Derived::g())所在記憶體位址其內容的值:*((int*)*(int*)&d+1)。

          最後看看兩個具體函數的情況,相信經過以上分析大家能很快填出他們的在記憶體中的地址(分別是虛函數表中表項的內容!):

          虛函數Derived::f()虛函數入口地址的值:*(int*)*(int*)&d,虛函數Derived::g()虛函數入口地址的值:*((int*)*(int*)&d+1)。

 

           下面我們就得到一張完整的虛函數表記憶體模型圖:

           下面進行測試代碼編寫,如下:

typedef void (*pFun)(void);int _tmain(int argc, _TCHAR* argv[]){Derived d;cout<<"d的首地址的值:"<<&d<<endl;cout<<"d首地址內容:"<<*(int*)&d<<endl;cout<<"虛函數表首地址的值、虛函數表第一表項所在記憶體位址的值:"<<(int*)*(int*)&d<<endl;cout<<"虛函數表第一表項內容、Derived::f()函數首地址的值:"<<*(int*)*(int*)&d<<endl;cout<<"虛函數表第二表項地址的值:"<<(int*)*(int*)&d+1<<endl;cout<<"虛函數表第二表項內容、Derived::g()函數首地址的值:"<<*((int*)*(int*)&d+1)<<endl;pFun p=(pFun)(*(int*)*(int*)&d);p();p=(pFun)(*((int*)*(int*)&d+1));p();int i;cin>>i;return 0;}

運行結果:

那麼到底是每個類在記憶體中只有一張虛函數表還是每個類的執行個體都儲存一張虛函數表呢,測試一下吧:

typedef void (*pFun)(void);int _tmain(int argc, _TCHAR* argv[]){Derived d;cout<<"d的首地址的值:"<<&d<<endl;cout<<"d首地址內容:"<<*(int*)&d<<endl;cout<<"虛函數表首地址的值、虛函數表第一表項所在記憶體位址的值:"<<(int*)*(int*)&d<<endl;cout<<"虛函數表第一表項內容、Derived::f()函數首地址的值:"<<*(int*)*(int*)&d<<endl;cout<<"虛函數表第二表項地址的值:"<<(int*)*(int*)&d+1<<endl;cout<<"虛函數表第二表項內容、Derived::g()函數首地址的值:"<<*((int*)*(int*)&d+1)<<endl;Derived d2;cout<<"d的首地址的值:"<<&d2<<endl;cout<<"d首地址內容:"<<*(int*)&d2<<endl;cout<<"虛函數表首地址的值、虛函數表第一表項所在記憶體位址的值:"<<(int*)*(int*)&d2<<endl;cout<<"虛函數表第一表項內容、Derived::f()函數首地址的值:"<<*(int*)*(int*)&d2<<endl;cout<<"虛函數表第二表項地址的值:"<<(int*)*(int*)&d2+1<<endl;cout<<"虛函數表第二表項內容、Derived::g()函數首地址的值:"<<*((int*)*(int*)&d2+1)<<endl;int i;cin>>i;return 0;}

運行結果:

由此可見d和d1的虛函數表地址一樣,兩個虛函數函數f()和g()的函數入口地址也一樣。

結論:每個存在虛函數的類在記憶體中有且只儲存一張虛函數表。

PS:其實*(int*)&d和(int*)*(int*)&d其實列印的值是一樣的,只是前者用10進位表示,後者用16進位表示的。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.