Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/152.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 多重继承中虚拟表的理解_C++ - Fatal编程技术网

C++ 多重继承中虚拟表的理解

C++ 多重继承中虚拟表的理解,c++,C++,我有一个类实现了两个抽象类,如下所示。没有虚拟继承。没有数据成员 class IFace1 { public: virtual void fcn(int abc) = 0; }; class IFace2 { public: virtual void fcn1(int abc) = 0; }; class RealClass: public IFace1, public IFace2 { public: void fcn(int a) { } voi

我有一个类实现了两个抽象类,如下所示。没有虚拟继承。没有数据成员

class IFace1 {
public:
    virtual void fcn(int abc) = 0;
};

class IFace2 {
public:
    virtual void fcn1(int abc) = 0;
};

class RealClass: public IFace1, public IFace2 {
public:
    void fcn(int a) {
    }

    void fcn1(int a) {
   }
};
我发现RealClass的vtable和object内存布局如下所示

Vtable for RealClass
RealClass::_ZTV9RealClass: 7u entries
0     (int (*)(...))0
8     (int (*)(...))(& _ZTI9RealClass)
16    (int (*)(...))RealClass::fcn
24    (int (*)(...))RealClass::fcn1
32    (int (*)(...))-8
40    (int (*)(...))(& _ZTI9RealClass)
48    (int (*)(...))RealClass::_ZThn8_N9RealClass4fcn1Ei

Class RealClass
    size=16 align=8
    base size=16 base align=8
RealClass (0x2af836d010e0) 0
    vptr=((& RealClass::_ZTV9RealClass) + 16u)
    IFace1 (0x2af836cfa5a0) 0 nearly-empty
        primary-for RealClass (0x2af836d010e0)
    IFace2 (0x2af836cfa600) 8 nearly-empty
        vptr=((& RealClass::_ZTV9RealClass) + 48u)

我对此感到困惑。什么是RealClass::\u ZThn8\u N9RealClass4fcn1Ei?为什么IFace2的vptr指出了这一点?当我从IFace2*调用fcn1时会发生什么?程序如何在RealClass的Vtable中找到RealClass::fcn1?我想它一定需要使用IFace2 vptr,但不清楚具体如何使用。

警告:下面的大部分内容当然是实现和平台相关的,并且是简化的。我将遵循在您的示例中实现它的方式——可能是GCC,64位


首先,虚拟类实例的契约是什么?例如,如果您有一个变量
IFace1*obj

  • obj+0有一个指向虚拟表的指针
  • 任何成员数据字段都将在obj+8处继续(
    sizeof(void*)
  • 虚拟表包含一条指向
    void fcn(int)
    atvtbl+0的记录
  • 在该表中,还有一个指针指向
    vtbl-8处的类的
    typeinfo
    (由
    dynamic\u cast
    等使用)和vtbl-16处的“基偏移”
任何看到类型为
IFace1*
的变量的函数都可以依赖于该值为真。对于
IFace2*
,情况类似

  • 如果他们想调用虚拟函数
    void fcn(int)
    ,他们查看obj+0以获取vtable,然后在vtbl+0调用在那里找到的地址<代码>此
    设置为obj
  • 如果他们想访问成员字段(自己访问,例如,如果该字段具有公共访问权限,或者如果有内联访问器),他们只需在其地址obj+xxx读取/写入成员即可
  • 如果他们想知道自己真正拥有的类型,他们会从对象的地址中减去vtbl-16的值,然后查看基本对象引用的vtable的
    typeinfo
    指针

现在,对于具有多重继承的类,编译器如何满足这些要求

1)首先,它需要为自己生成结构。虚拟表指针必须位于obj+0处,所以它就在那里。这张桌子看起来怎么样?很明显,基的偏移量是0,很容易生成
typeinfo
数据和指向它的指针,然后是第一个虚函数和第二个虚函数,没有什么特别的。任何知道
RealClass
定义的人都可以进行相同的计算,因此他们知道在vtable等中的何处可以找到函数

2)然后它可以让
RealClass
作为
IFace1
传递。因此,它需要在对象中的某个位置有一个指向
IFace1
格式的虚拟表的指针,那么虚拟表必须有一条
void fcn(int)
的记录

编译器很聪明,可以重用它生成的第一个虚拟表,因为它符合这些要求。如果有任何成员字段,它们将存储在指向虚拟表的第一个指针之后,因此即使是它们也可以像派生类是基类一样简单地访问。到目前为止还不错

3)最后,如何处理该对象,以便其他人能够将其用作
IFace2
?已创建的vtable不能再使用,因为
IFace2
需要其
void fcn1(int)
位于vtbl+0

因此,将创建另一个虚拟表,即在转储中第一个虚拟表之后立即看到的虚拟表,并且指向它的指针存储在下一个可用位置的
RealClass
中。第二个表需要将“偏移到基准”设置为-8,因为实际对象从偏移量-8开始。它只包含指向
IFace2
虚拟函数的指针,
void fcn1(int)

然后,对象中的虚拟指针(偏移量obj+8)后面是
IFace2
的任何成员数据字段,这样,当指针指向此接口时,任何继承或内联函数都可以再次工作


好的,现在如何从
IFace2
调用
fcn1()
?对RealClass::fcn1(int)的非虚拟thunk是什么

如果您将
RealClass*
指针传递给一个使用
IFace2*
的陌生函数,编译器将发出代码将指针增加8(或无论多么大的
sizeof(void*)+sizeof(IFace1)
),以便该函数获取以
IFace2的虚拟表指针开始的指针,然后是它的成员字段——正如我前面概述的合同中所约定的

当该函数想要调用
void IFace2::fcn1(int)
时,它会查看虚拟表,找到该特定函数的记录(第一个也是唯一一个)并调用它,将
this
设置为作为指向
IFace2
的指针传递的地址

这里出现了一个问题:如果有人在
RealClass
指针上调用
RealClass
中实现的这个方法,
this
指向
RealClass
的底部。与
IFace1
相同。但是,如果有人使用指向
IFace2
接口的指针调用它,
会将8个字节(或多个字节)指向对象

因此,编译器需要多次生成函数以适应这种情况,否则它无法正确访问成员字段和其他方法,因为它根据调用方法的人而有所不同

编译器optimi没有让代码真正重复两次