2012-07-22 108 views
4

因此,回到基礎,我試圖圍繞vtables和whatnot。在下面的例子中,如果我要將B*傳遞給某個函數,那麼該函數如何知道調用C對象的vtable中的方法,而不是來自A的vtable的方法?是否有兩個傳遞給該對象的單獨的VTables?接口指針真的只是vtable(因爲接口,IIRC,不能包含屬性聲明)?C++多繼承和虛擬桌面

我想說的是,直到我真的試過這段代碼,我一直在假設你不能從一個接口繼承多個接口/類(並且所有的接口都有可以說是線性的),以便vtable建立在它自己之上。

如果我的虛函數表是如何工作的想法是正確(我現在知道這是不是),然後通過一個B*,並呼籲B::OutB()會叫A:OutA()而不是(這顯然並非如此)。

有人可以發光嗎?

// Includes 
#include <windows.h> 
#include <iostream> 

interface A 
{ 
public: 
    virtual void OutA() = 0; 
}; 

interface B 
{ 
public: 
    virtual void OutB() = 0; 
}; 

class C : public A, public B 
{ 
public: 
    void OutA(); 
    void OutB(); 
}; 

void C::OutA() 
{ 
    printf("Out A\n"); 
} 

void C::OutB() 
{ 
    printf("Out B\n"); 
} 

int main() 
{ 
    C obj; 
    obj.OutA(); 
    obj.OutB(); 

    A* ap = (A*)&obj; 
    B* bp = (B*)&obj; 

    ap->OutA(); 
    bp->OutB(); 

    system("pause"); 

    // Return 
    return 0; 
} 

輸出(如預期):

Out A 
Out B 
Out A 
Out B 
+4

維基文件GCC的虛函數表的格式,並且應該回答你的問題:HTTP: //en.wikipedia.org/wiki/Virtual_method_table#Example。 「多重繼承」部分也直接回答你的問題。 – Saul 2012-07-22 14:28:37

+0

http://www.openrce.org/articles/files/jangrayhood.pdf「引擎蓋下的C++」是您的參考資料。看到這個問題也http://stackoverflow.com/questions/1963926/when-is-av-table-created-in-c – 2012-07-22 18:51:15

+1

微軟有一個很好的參考,不僅詳細列出了vtables的佈局,而且還進入了所需的修復將一種類型的指針轉​​換爲另一種類型的指針,包括所有的邊緣情況。不幸的是,我幾年來一直無法找到它。 – 2012-07-24 15:47:00

回答

10

我不知道的interface是的,但如果你在寫C++和AB爲類,那麼C將包含兩個子對象:AB,並且這些子對象的將有自己的虛函數表指針。

編譯C++到C,我們可以有:

#include <stdio.h> 
#include <stddef.h> 
#include <stdlib.h> 

const int debug = 0; 

void __pure_virtual_called() { 
    fputs ("pure virtual function called\n", stderr); 
    abort(); 
} 

/* Translation of: 

class A 
{ 
public: 
    virtual void OutA() = 0; 
}; 
*/ 

struct A; 

typedef struct { 
    void (*ptr__OutA) (struct A *__this); 
} vtable__A; 

typedef struct A { 
    vtable__A *__vptr; 
} A; 

/* translation A::OutA() 
* pure virtual function */ 
void A__OutA (A *__this) { 
    __pure_virtual_called(); 
} 

vtable__A vtable__A__A = { .ptr__OutA = A__OutA }; 

void A__constructor (A *__this) { 
    if (debug) 
     printf ("A__constructor %p\n", (void*)__this); 

    /* dynamic type is initialised to A */ 
    __this->__vptr = &vtable__A__A; 
} 

/* Translation of: 

class B 
{ 
public: 
    virtual void OutB() = 0; 
}; 

*/ 

struct B; 

typedef struct { 
    void (*ptr__OutB)(struct B *__this); 
} vtable__B; 

typedef struct B { 
    vtable__B *__vptr; 
} B; 

/* translation B::OutB() 
* pure virtual function */ 
void B__OutB (B *__this) { 
    __pure_virtual_called(); 
} 

vtable__B vtable__B__B = { .ptr__OutB = B__OutB }; 

void B__constructor (B *__this) { 
    if (debug) 
     printf ("B__constructor %p\n", (void*)__this); 

    /* dynamic type is initialised to B */ 
    __this->__vptr = &vtable__B__B; 
} 
/* Translation of: 

class C : public A, public B 
{ 
public: 
    void OutA(); // overrides A::OutA() 
    void OutB(); // overrides B::OutB() 
    // note : 
    // no new virtual function 
}; 

*/ 

/* no new virtual function 
* so no specific vtable type! */ 

typedef struct { 
/* no additional vptr, we already have 2! */ 
    A base__A; 
    B base__B; 
} C; 

/******* upcasts 
* translation of 
* static_cast<C*> (p) 
*/ 

/* translation of 
* A *p; 
* static_cast<C*> (p); 
*/ 
C *static_cast__A__C (A *__ptr) { 
    /* 
    * base__A is first member of C 
    * so offsetof(C, base__A) == 0 
    * can skip the pointer adjustment 
    */ 
    return (C*)__ptr; 
} 

/* translation of 
* B *p; 
* static_cast<C*> (p); 
*/ 
C *static_cast__B__C (B *__ptr) { 
    /* locate enclosing C object: 
    * __base__B is not first member 
    * need to adjust pointer 
    */ 
    return (C*)((char*)__ptr - offsetof(C, base__B)); 
} 

/* translation of virtual functions of C 
* overriding function declarations from A 
*/ 

/* translation of C::OutA() */ 

/* C::OutA() called from C */ 
void C__OutA (C *__this) { 
    printf("Out A this=%p\n", (void*)__this); 
} 

/* C::OutA() called from A */ 
void C__A__OutA (A *__this) { 
    if (debug) 
      printf ("C__A__OutA %p\n", (void*)__this); 
    C__OutA (static_cast__A__C (__this)); 
} 

vtable__A vtable__A__C = { .ptr__OutA = C__A__OutA }; 

/* translation of virtual functions of C 
* overriding function declarations from B 
*/ 

/* translation of C::OutB() */ 

/* C::OutB() called from C */ 
void C__OutB (C *__this) { 
    printf("Out B this=%p\n", (void*)__this); 
} 

/* C::OutB() called from B */ 
void C__B__OutB (B *__this) { 
    if (debug) 
      printf ("C__B__OutB %p\n", (void*)__this); 
    C__OutB (static_cast__B__C (__this)); 
} 

vtable__B vtable__B__C = { .ptr__OutB = C__B__OutB }; 

void C__constructor (C *__this) { 
    if (debug) 
     printf ("C__constructor %p\n", (void*)__this); 
    /* construct subobjects */ 
    A__constructor (&__this->base__A); 
    B__constructor (&__this->base__B); 

    /* adjust dynamic type of this to C */ 
    __this->base__A.__vptr = &vtable__A__C; 
    __this->base__B.__vptr = &vtable__B__C; 
} 

/* calls to C virtual functions with a C* 
*/ 

/* translation of 
* C *p; 
* p->OutA(); 
* 
* is 
* ((A*)p)->OutA(); 
* 
* because C::OutA() is overrides A::OutA() 
*/ 
void dyn__C__OutA (C *__this) { 
    A *base_ptr__A = &__this->base__A; 
    base_ptr__A->__vptr->ptr__OutA (base_ptr__A); 
} 

/* translation of 

int main() 
{ 
    C obj; 
    obj.OutA(); 
    obj.OutB(); 

    A *ap = &obj; 
    B *bp = &obj; 
    C *cp = &obj; 

    ap->OutA(); 
    bp->OutB(); 
    cp->OutA(); 

    // Return 
    return 0; 
} 

* 
*/ 

int main() { 
    /* translation of: 
    C obj; 
    */ 
    C obj; 
    C__constructor (&obj); 

    /* translation of: 
    obj.OutA(); 
    obj.OutB(); 
    * obj is a locally declared object 
    * so dynamic type of obj is known as C 
    * can make direct call to C::OutA(), C::OutB() 
    */ 
    C__OutA (&obj); 
    C__OutB (&obj); 

    /* dumb (zero optimisation) translation of: 
    A *ap = &obj; 
    B *bp = &obj; 
    C *cp = &obj; 
    */ 
    A *ap = &obj.base__A; 
    B *bp = &obj.base__B; 
    C *cp = &obj; 

    /* translation of: 
    ap->OutA(); 
    bp->OutB(); 
    cp->OutA(); 

    * dumb compiler = no optimisation 
    * so dynamic type of *ap, *bp, *cp is unknown 
    * so make "virtual" calls using vtable 
    */ 
    ap->__vptr->ptr__OutA(ap); 
    bp->__vptr->ptr__OutB(bp); 
    dyn__C__OutA (cp); 

    /* note: obj lifetime ends now 
    * C has a trivial destructor 
    * so no destructor call needed 
    */ 

    return 0; 
} 

http://ideone.com/TioyX

輸出:

Out A this=0xbfeee2ec 
Out B this=0xbfeee2ec 
Out A this=0xbfeee2ec 
Out B this=0xbfeee2ec 
Out A this=0xbfeee2ec 
+0

做得非常好! – aschepler 2012-07-24 16:34:07

+0

偉大的參考,這正是我正在尋找! – Qix 2012-07-24 18:00:28

+1

哇,這是真棒和驚人的信息。我有點想知道這是如何看待多層次的繼承,雖然u.u – Llamageddon 2014-10-19 03:28:25

3

隨着多重繼承,所述對象被內置部件,對應於基礎類中的一個的每個部分。這包括vtable指針。這是必要的,因爲與指針或引用交互的代碼不知道它是否與基類或派生類一起工作,因此它們必須以相同的方式佈局。

一個令人驚訝的結果是,當您將一個指針指向其中一個基類時,其地址可能會改變!編譯器在幕後生成一些代碼來調整指向適當對象的指針。

C obj; 
A* ap = (A*)&obj; 
B* bp = (B*)&obj; 
bool same = ((void*)ap) == ((void*)bp); // false!