C++虛函數表分析

來源:互聯網
上載者:User

標籤:poi   clu   log   func   子類   32位   lis   art   inux   

先看代碼:

#include <iostream>using namespace std;class Base {public:    virtual void f() {cout<<"base::f"<<endl;}    virtual void g() {cout<<"base::g"<<endl;}    virtual void h() {cout<<"base::h"<<endl;}};class Derive : public Base{public:    void g() {cout<<"derive::g"<<endl;}};//可以稍後再看int main () {    cout<<"size of Base: "<<sizeof(Base)<<endl;    typedef void(*Func)(void);    Base b;    Base *d = new Derive();    long* pvptr = (long*)d;    long* vptr = (long*)*pvptr;    Func f = (Func)vptr[0];    Func g = (Func)vptr[1];    Func h = (Func)vptr[2];    f();    g();    h();    return 0;}

 

    都知道C++中的多態是用虛函數實現的: 子類覆蓋父類的虛函數, 然後聲明一個指向子類對象的父類指標, 如Base *b = new Derive();
當調用b->f()時, 調用的是子類的Derive::f()。 
這種機制內部由虛函數表實現,下面對虛函數表結構進行分析,並且用GDB驗證。
 
1. 基礎知識:
(1) 32位os 指標長度為4位元組, 64位os 指標長度為8位元組, 下面的分析環境為64位 linux & g++ 4.8.4.
(2) new一個對象時, 只為類中成員變數分配空間, 對象之間共用成員函數。

2. _vptr
    運行下上面的代碼發現sizeof(Base) = 8, 說明編譯器在類中自動添加了一個8位元組的成員變數, 這個變數就是_vptr, 指向虛函數表的指標。
_vptr有些文章裡說gcc是把它放在對象記憶體的末尾,VC是放在開始, 我編譯是用的g++,驗證了下是放在開始的:

驗證代碼:取對象a的地址與a第一個成員變數n的地址比較,如果不等,說明對象地址開始放的是_vptr. 也可以用gdb直接print a 會發現_vptr在開始

class A{public:      int n;      virtual void Foo(void){}};int main(){     A a;     char *p1 = reinterpret_cast<char*>(&a);     char *p2 = reinterpret_cast<char*>(&a.n);     if(p1 == p2)     {        cout<<"vPtr is in the end of class instance!"<<endl;     }else     {        cout<<"vPtr is in the head of class instance!"<<endl;     }     return 1;}

(3) 虛函數表
    包含虛函數的類才會有虛函數表, 同屬於一個類的對象共用虛函數表, 但是有各自的_vptr.
    虛函數表實質是一個指標數組,裡面存的是虛函數的函數指標。

Base中虛函數表結構:

Derive中虛函數表結構:

 

(4)驗證
運行上面代碼結果:
    size of Base: 8
    base::f
    derive::g
    base::h

說明Derive的虛函數表結構跟上面分析的是一致的:
    d對象的首地址就是vptr指標的地址-pvptr,
    取pvptr的值就是vptr-虛函數表的地址
    取vptr中[0][1][2]的值就是這三個函數的地址
    通過函數地址就直接可以運行三個虛函數了。
    函數表中Base::g()函數指標被Derive中的Derive::g()函數指標覆蓋, 所以執行的時候是調用的Derive::g()

 

(5)多繼承

 

 

附 GDB調試:(1) #產生帶有調試資訊的可執行檔g++ test.cpp -g -o test    (2) #載入testgdb test(3) #列出Base類代碼(gdb) list Base1       #include <iostream>23       using namespace std;45       class Base {6       public:7           virtual void f() {cout<<"base::f"<<endl;}8           virtual void g() {cout<<"base::g"<<endl;}9           virtual void h() {cout<<"base::h"<<endl;}10      };4) #查看Base函數地址(gdb) info line 7 Line 7 of "test.cpp" starts at address 0x400ac8 <Base::f()> and ends at 0x400ad4 <Base::f()+12>.(gdb) info line 8Line 8 of "test.cpp" starts at address 0x400af2 <Base::g()> and ends at 0x400afe <Base::g()+12>.(gdb) info line 9Line 9 of "test.cpp" starts at address 0x400b1c <Base::h()> and ends at 0x400b28 <Base::h()+12>.5)#列出Derive代碼(gdb) list Derive 7           virtual void f() {cout<<"base::f"<<endl;}8           virtual void g() {cout<<"base::g"<<endl;}9           virtual void h() {cout<<"base::h"<<endl;}10      };1112      class Derive : public Base{13      public:14          void g() {cout<<"derive::g"<<endl;}15      };6)#查看Derive函數地址(gdb) info line 14Line 14 of "test.cpp" starts at address 0x400b46 <Derive::g()> and ends at 0x400b52 <Derive::g()+12>.7)#start執行程式,n逐步執行(gdb) startTemporary breakpoint 1, main () at test.cpp:1919          cout<<"size of Base: "<<sizeof(Base)<<endl;(gdb) nsize of Base: 822          Base b;(gdb)23          Base *d = new Derive();(gdb)25          long* pvptr = (long*)d;(gdb)26          long* vptr = (long*)*pvptr;(gdb)27          Func f = (Func)vptr[0];(gdb)28          Func g = (Func)vptr[1];(gdb)29          Func h = (Func)vptr[2];(gdb)31          f();(gdb)8) #print d對象, 0x400c90為成員變數_vptr的值,也就是函數表的地址(gdb) p *d $4 = {_vptr.Base = 0x400c90 <vtable for Derive+16>}(gdb) p vptr$6 = (long *) 0x400c90 <vtable for Derive+16>(9) #查看函數表值,與之前查看函數地址一致(gdb) p (long*)vptr[0]$9 = (long *) 0x400ac8 <Base::f()>(gdb) p (long*)vptr[1]$10 = (long *) 0x400b46 <Derive::g()>(gdb) p (long*)vptr[2]$11 = (long *) 0x400b1c <Base::h()>

 

另vptr. vtable記憶體位置, refer  http://www.tuicool.com/articles/iUB3Ebi

C++虛函數表分析

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.