2013-07-31 50 views
3
#include<iostream> 

using namespace std; 

class X 
{ 
    int a; 
    int b; 
    public: 
    void f(int a) 
    { 
     cout<<"\nInside X"; 
    } 


    virtual void abc() 
    { 
     cout<<"\nHello X"; 
    } 

}; 


class Y : public X 
{ 
    int a; 
    public: 

    void f(int a, int b) 
    { 
     cout<<"\nInside Y"; 
    } 


    void abc() 
    { 
     cout<<"\nHello Y"; 
    } 

}; 



int main() 
{ 

    X a; 

    cout<<sizeof(X); 

    Y b; 

    cout<<sizeof(Y); 

    X *h = new Y; 

    h->abc(); 
} 

據我所知,類X的大小爲12字節的原因是因爲它包含一個虛擬表的vptr(虛擬指針)。無論如何,我可以閱讀這個虛擬表,如果沒有,至少可以訪問虛擬指針。我試過使用工會,但它給了我一些錯誤。理解虛函數的麻煩

而且,當我打電話H-> ABC(),它是如何知道的類的對象,h指向?我認爲這大部分是在編譯時完成的。但是當你有一個指向派生類的基類指針時,它如何知道要執行哪個類函數。 考慮這兩種情況

X *h = new X; 
h->abc();/* This would call the abc function in X */ 

X *h = new Y; 
h->abc();/* This would call the abc function in Y*/ 

我看了,那h指針會去它指向的對象的虛函數表,並會因此調用該函數?但是,這在運行時如何實現?

+1

我會編輯最後一個問題,因爲它不會與討論主題一起進行。但我相信前兩個問題都在本次討論的範圍之內。 – user2560730

回答

2
  1. 除非您確定自己在做什麼,否則不應嘗試訪問vtable指針。就通常用於定義程序含義的語言而言,vtable甚至不存在。它是一個實現細節,它屬於實現(a.k.a.編譯器和運行時環境)。

    如果實現由便攜式ABI(應用程序二進制接口)的約束,那麼ABI會說在哪裏可以找到虛函數表指針,什麼是虛函數表內。 reinterpret_cast< vtable const * const & >(my_obj)應該訣竅從任何「合理的」ABI上的對象獲取指針。

    這樣的程序將被約束到一個平臺和一個ABI。 (C++ ABI接口往往比其他語言的改變往往比C,但不經常。)根據ABI是一個貧窮的設計選擇,除非你只是想證明你瘋了。

  2. V表標識派生類 - 這是它的目的。它包含指向派生類覆蓋基類的函數的指針。它還包含一個結構,其中包含派生類的名稱並鏈接到它的基地,以便動態確定派生類的派生類。

    使用dynamic_cast確定推導和找到派生對象的算法,實際上可能是驚人的慢 - 不O(1)。它通常必須搜索基類的鏈接結構。

6

好吧你的第一個問題:我給你一個可能會更好理解的例子! enter image description here

#include<iostream> 
using namespace std; 
class Base1 { 

    public: 

     int ibase1; 

     Base1():ibase1(10) {} 

     virtual void f() { cout << "Base1::f()" << endl; } 

     virtual void g() { cout << "Base1::g()" << endl; } 

     virtual void h() { cout << "Base1::h()" << endl; } 
}; 

class Base2 { 

    public: 

     int ibase2; 

     Base2():ibase2(20) {} 

     virtual void f() { cout << "Base2::f()" << endl; } 

     virtual void g() { cout << "Base2::g()" << endl; } 

     virtual void h() { cout << "Base2::h()" << endl; } 

}; 

class Base3 { 

    public: 

     int ibase3; 

     Base3():ibase3(30) {} 

     virtual void f() { cout << "Base3::f()" << endl; } 

     virtual void g() { cout << "Base3::g()" << endl; } 

     virtual void h() { cout << "Base3::h()" << endl; } 
}; 

class Derive : public Base1, public Base2, public Base3 { 

    public: 

     int iderive; 

     Derive():iderive(100) {} 

     virtual void f() { cout << "Derive::f()" << endl; } 

     virtual void g1() { cout << "Derive::g1()" << endl; } 

}; 

這其中實施了三個基類BASE1,BASE2,base3,在那裏你派生類的內存等級:

Base1 *p1 = new Derive(); 
Base2 *p2 = new Derive(); 
Base3 *p3 = new Derive(); 

P1將指向vtale1,P2將指向vtable2, p3將指向vtable3,如果你調用一些虛擬函數,它會找到非常虛擬的表並獲取地址!

在您的代碼:

X *h = new Y; 

h會指向開始Y的記憶,這是X的虛擬表的位置,他會發現abc()其中Y中實現的地址!

你的第二個問題:

的編譯器會考慮爲正常的功能,所以它把成員函數的地址在code section,所以它不會佔用的內存成員函數!

如果你想讀的虛擬表你可以嘗試這樣的:這是我在我的例子試圖gcc4.7

typedef void(*Func)(void); 
    Derive d; 
    int **pd = (int **)(&d); 
    int i = 0; 
    while(i < 4) 
    { 
     Func f = (Func)pd[0][i]; 
     f(); 
     i++; 
    } 
    int s = (int)(pd[1]); 
    cout << s << endl; 
    i = 0; 
    cout << "===============================================" << endl; 
    while(i < 3) 
    { 
     Func f = (Func)pd[2][i]; 
     f(); 
     i++; 
    } 
    s = (int)(pd[3]); 
    cout << s << endl; 
    cout << "===============================================" << endl; 
    i = 0; 
    while(i < 3) 
    { 
     Func f = (Func)pd[4][i]; 
     f(); 
     i++; 
    } 
    s = (int)(pd[5]); 
    cout << s << endl; 
    s = (int)(pd[6]); 
cout << s << endl; 

,你會得到的結果如下:

Derive::f() 
Base1::g() 
Base1::h() 
Derive::g1() 
10 
=============================================== 
Derive::f() 
Base2::g() 
Base2::h() 
20 
=============================================== 
Derive::f() 
Base3::g() 
Base3::h() 
30 
100 
1

反正我能讀懂這個虛擬表

不是真的,無覆蓋不只要知道指針所針對的是與編譯器相關的對象指針值即可。

如果沒有,至少可以訪問虛擬指針。

爲什麼?你可以通過h->abc得到該功能的地址,那是你想要的嗎?

另外,當我打電話給h-> abc()時,它是如何知道類的對象,h是指向的?

它並不是真的,它只是知道該類的vtable在哪裏。如果你使用RTTI,vtable中有信息告訴它什麼類,但不需要調用虛擬函數。從X派生的每個類都有自己的vtable,它包含自己的虛擬函數的指針。 (總是假設當然是基於vtable的實現。)

我讀過,h指針會轉到它指向的對象的vtable,並因此調用該函數?但是,這在運行時如何實現?

你剛纔自己描述了它。爲了稍微詳細說明,指針h->abc解析爲h->_vtable[x]的某些常量x,其代表abc的虛擬函數指針的vtable中的偏移量。所以電話解析爲*(h->_vtable[abc])(...)

另一個問題,與我需要清理的虛函數無關。如果函數具有像其他變量一樣的地址,爲什麼它們不佔用對象中的空間?

他們爲什麼要這樣做?這意味着每個對象中的每個函數的副本。重點是什麼?