2013-03-28 64 views
2

我有一些訪問派生和基地ptrs的代碼,當我打印出地址時,它們是相同的。如果是這種情況,編譯器如何知道它所指的地址是A還是B?編譯器如何確定它正在尋址哪個多態類型

下面是代碼:

#include <iostream> 

class A 
{ 
public: 
    A() : val_(0) { 
     ptrA = this; 
    } 

    virtual void set(int val) { val_ = val; } 

    virtual void printval() = 0; 

    static A* ptrA; 

    int val_; 
}; 

class B : public A 
{ 
public: 
    B() : A() { 
     ptrB = this; 
    } 

    virtual void printval() { std::cout << A::val_ << std::endl; } 

    static B* ptrB; 
}; 

A* A::ptrA = 0; 
B* B::ptrB = 0; 

int main() { 

    A* p = new B(); 
    p->set(3); 
    p->printval(); 

    std::cout << "A part address=" << A::ptrA << std::endl; 
    std::cout << "B part address=" << B::ptrB << std::endl; 

    return 0; 
} 

這裏是打印輸出:

A part address=00501F40 
B part address=00501F40 

兩個地址完全相同。編譯器是否也存儲一些額外的信息?

編輯: 是的我想說的是,程序在運行時如何知道。

+0

您確實是指「編譯器如何知道」?或者你的意思是「**編譯的代碼**怎麼知道的?」?基本上,你是在談論編譯時還是運行時? –

+1

你的問題的基本答案是「[vtable]」* – paulsm4

+0

*編譯器*通常**不會**知道它所處理的是什麼多態類型,這是多態的一個主要屬性。在運行時,vtable被解析爲指向編譯器可能不知道的東西。 – ssube

回答

0

在效果上,假設一個「正常」 C實現如你的,多類B的數據的是一個結構,並且所述第一構件該結構是類A的對象。由於它包含的B和A開始於同一個地方,它們具有相同的地址。

因爲在B的開始處有一個A對象,所以可以將指向B對象的指針轉換爲指向A對象的指針,並且它像A對象一樣工作,因爲它是:它是指向數據。

虛擬功能比較複雜。在A數據中,通常對您而言不可見的是指向表的指針。如果該對象實際上是一個普通的A對象,則該指針將指向具有A的虛擬函數地址的表。如果對象是B對象,則該指針指向具有虛擬地址的表函數爲B.結果是,如果你有一個指針的編譯時類型看起來像指向A,編譯器通過在表中查找它們的地址來調用它的函數。如果指向對象的實際類型爲B,則此表提供了B的虛擬函數的地址。

+0

我正在將你的標記作爲答案,因爲你也解釋了'爲什麼地址是同一部分' –

2

內置的額外信息通過實現依賴機制。當你編譯你的程序時,編譯器悄悄地添加它所需的所有代碼。
對於幾乎所有的編譯器來說,動態調度都是通過一個虛擬表和指針來實現的。

食品閱讀:

What happens in the hardware when I call a virtual function? How many layers of indirection are there? How much overhead is there?

+0

虛擬表是類的一部分,而指向表的指針是對象的隱藏元素。你可以通過'sizeof'看到指針的效果。 –

+0

IIRC,在MSVC中,指向vtable的指針是該類的第一個元素。然後,vtable本身就是一個指針數組(鍵入方法順序)。這用於一些體面的軟件來在運行時掛鉤虛擬呼叫。 – ssube

相關問題