当我怀疑这个时,我正在阅读有关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”?
编译器如何根据传递的对象类型正确计算“int a”的地址?
答案 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。这就是多态性的工作原理。