2013-03-28 23 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

相关问题