编译器如何解析访问非虚拟数据成员C ++的虚函数

时间:2013-10-03 02:16:29

标签: c++ inheritance

当我怀疑这个时,我正在阅读有关C ++对象的内容。假设有两个类

class X
{
     virtual int def() { };
}

class Y
{
     virtual int abc() { };
}
class Z : public X,public Y
{
     virutal int abc() { return a };
     int a;
}

现在我所理解的是Y和Z都在其虚拟表中引用了Z :: abc(),这有助于解析正确的函数来调用。考虑

Y *y = new Z;
Z *z = new Z;
y->abc() // I understand this is done by reaching the vptr of y by this = this + sizeof (X)
and z->abc() // z has its own vptr reachable

我的理解是,在这两种情况下都传递了“this”指针,在找到正确的abc()来调用之后,程序如何达到int值“a”? enter image description here

编译器如何根据传递的对象类型正确计算“int a”的地址?

3 个答案:

答案 0 :(得分:3)

这是所有实现细节,不同的编译器做不同的事情。这个问题有两种常见的方法,但都取决于修复this指针在函数定义中引用的内容。

一种方法是让this指针始终引用完整对象(至少在该虚函数的最终覆盖级别)。在这种方法中,并且在存在多重继承的情况下,vtable中的条目不包含指向实际函数的指针,而是指向调整this指针然后跳转到覆盖的蹦床的指针。这种方法的优点是,对于最左边的非空基,并且在所有单继承的情况下,vtable中的条目实际上是覆盖,并且没有相关的成本。这是Itanium C ++ ABI中采用的方法,它在不同的操作系统中使用,包括Linux。

另一种方法是始终传递首先声明成员函数的子对象的地址。在这种情况下,调用者调整this指针以在跳过vtable之前引用子对象。与第一种情况一样,如果没有偏移量(第一个非空基,单个继承),编译器不需要添加任何调整,也不会产生任何成本。在最终覆盖的实现中,编译器使用声明函数的基指针的偏移量,而不是整个对象的偏移量。我相信在Windows / Visual Studio中就是这种情况,但不要在这里听取我的意见,因为我无法访问VS C ++ ABI来确认这一点。

第三种方法是直接在vtable中存储调整。这种方法不太常见,即使不需要更新(通过加0)也会产生调整偏移的成本。我不知道任何使用这种方法的当前编译器。

答案 1 :(得分:1)

你可以治疗

 virtual int abc() { return a };

作为

 virtual int abc(Z * const this) { return this->a };

因此,对于每个非静态成员函数,都有一个hidden指针指向该对象。因此,只要编译器可以找到虚拟方法,它就知道a的位置。

答案 2 :(得分:0)

基于John的观点,如果派生类重写基类方法,编译器将自动创建所谓的派生类中的vtable的vpointer。派生类中幕后的this指针指向vpointer,然后指向派生类的vtable。这就是多态性的工作原理。