我决定了解vtable是如何构建的,我打开了调试器,发现了一些奇怪的事情。节点ptr包含一些vptr。我一直认为每个对象只有一个vptr。有人能跟我解释一下这是怎么回事吗?(我指基类指针指向派生类的对象时)
#include <iostream>
using namespace std;
class Base
{
int base;
public:
virtual void say()
{
cout << "Hello" << endl;
}
virtual void no()
{
cout << "No" << endl;
}
};
class Base2
{
public:
virtual void lol()
{
cout << "lol" << endl;
}
};
class Derv:public Base,public Base2
{
public:
void say()
{
cout << "yep" << endl;
}
};
int main()
{
Base* ptr = new Derv();
ptr->say();
ptr = new Base();
ptr->say();
}

发布于 2014-05-19 20:27:56
需要两个指针,因为有两个带有虚拟函数的基类。
让我们一步一步地看一遍:
首先定义具有虚拟函数的Base。因此,编译器将创建一个虚拟表,大致如下(括号中给出的索引;请注意,这是一个示例,确切的表布局将取决于编译器):
[0] address of Base::say()
[1] address of Base::no()在Base布局中,将有一个字段__vptr (或者不管它是如何命名的,如果它被命名的话)指向该表。当给定类型为pBase的指针Base*并要求调用say时,编译器实际上将调用(p->__vptr[0])()。
接下来,定义第二个独立类Base2,其虚拟表如下所示:
[0] address of Base2::lol()通过lol指针对Base2的调用现在将转换为类似于(pBase2->__vptr[0])()的内容。
现在,您终于定义了一个类Derv,它继承自Base和Base2。这尤其意味着您可以让Base*和Base2*同时指向Derv类型的对象。现在,如果只有一个__vptr,pBase->say()和pBase2->lol()将调用相同的函数,因为它们都转换为(pXXX->__vptr[0])()。
然而,实际发生的情况是,有两个__vptr字段,一个用于Base基类,一个用于_Base2基类。Base*用它的__vptr指向Base子对象,Base2*用它自己的__vptr指向Base2子对象。现在,Derv虚拟表看起来可能如下所示:
[0] address of Derv::say()
[1] address of Base::no()
[2] address of Base2::lol()__vptr的Base子对象指向该表的开头,而Base2子对象的__vptr指向元素[2]。现在调用pBase->say()将转换为(pBase->__vptr[0])(),并且由于Base子对象的__vptr指向Derv虚拟表的开头,因此它将按预期的方式调用Derv::say()。另一方面,如果调用pBase2->lol(),它将被转换为(pBase2->__vptr[0])(),但是由于pBase2指向Base2子代码>d48,因此它将取消相应的D 49,后者指向D 50的Derv的虚拟表,其中存储D52的地址。因此,现在Base2::lol()被称为预定地址。
发布于 2014-05-19 20:09:54
想想看,当将指针转换为指向基的指针时,它必须引用与基类型具有相同布局的内存块。当您拥有多个继承时,您将在每个具有虚拟函数的基中得到一个vptr。
https://stackoverflow.com/questions/23746150
复制相似问题