Closed. This question needs details or clarity。它当前不接受答案。












想改善这个问题吗?添加详细信息,并通过editing this post阐明问题。

6年前关闭。



Improve this question




我决定真正了解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();
}

c&#43;&#43; - 虚拟表指针-LMLPHP

最佳答案

因为您有两个带有虚函数的基类,所以需要两个指针。

让我们逐步进行一下:

首先定义具有虚拟功能的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类,该类继承自BaseBase2。这尤其意味着您可以同时拥有Base*Base2*指向Derv类型的对象。现在,如果您只有一个__vptrpBase->say()pBase2->lol()将调用相同的函数,因为它们都转换为(pXXX->__vptr[0])()

但是实际发生的是有两个__vptr字段,一个字段用于Base基类,而另一个字段用于_Base2基类。 Base*带有Base指向__vptr子对象,Base2*带有自己的Base2指向__vptr子对象。现在Derv虚拟表可能看起来像像这样:
[0] address of Derv::say()
[1] address of Base::no()
[2] address of Base2::lol()
__vptr子对象的Base指向该表的开头,而__vptr子对象的Base2指向元素[2]。现在调用pBase->say()将转换为(pBase->__vptr[0])(),并且由于__vptr子对象的Base指向Derv虚拟表的开头,因此最终将按预期方式调用Derv::say()。另一方面,如果您调用pBase2->lol(),它将被转换为(pBase2->__vptr[0])(),但是由于pBase2指向Base2子对象od Derv,因此它将取消引用对应的__vptr,后者指向[2]的虚拟表的元素Derv,其中Base2::lol已存储。因此,现在按预期调用了Base2::lol()

关于c++ - 虚拟表指针,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/23746150/

10-11 15:38