【问题标题】:Why virtual inheritance cause pointer offset?为什么虚拟继承会导致指针偏移?
【发布时间】:2020-12-08 10:27:15
【问题描述】:

我知道由于内存布局,多重继承和虚函数在将派生类指针转换为基类指针时会导致指针偏移。

但是我不明白为什么虚拟继承也会导致这种效果?我只知道虚拟继承是防止同一个类的多个实例。

下面是我的代码。

class X 
{
public:
    int i;
};

class Y :virtual  public X 
{
    int j;
public:
    void vf(){};
};

int main()
{
    Y* py = new Y;
    X* px = (X*)py;

    cout<<py<<endl;
    cout<<px<<endl;
}

【问题讨论】:

  • 请不要添加不相关的标签。那显然不是 C 代码。
  • px的值与py的值不同
  • “也会导致这个问题吗?”:不是问题吧?
  • 解释为什么你认为在这种情况下指针偏移是一个问题。建议Y 中(实际上)只有一个X 是一回事,而建议XY 应该在同一个地址是另一回事。想象一下如果Y 有多个虚拟基址X1X2 等会发生什么 - 你真的建议Y 的地址应该等于所有X1 的地址,@ 987654331@等????
  • 为了获得更多信息:同时打印对象内两个变量的地址(或相对偏移量)。间隙将显示 vtable 指针所在的位置。

标签: c++ pointers casting virtual-inheritance


【解决方案1】:

以下代码

#include <inttypes.h>
#include <stdio.h>
#include <stdint.h>

struct Base {
  long a;
};

struct Derived : virtual public Base {
  long b;
};

int main() {
  Derived object;
  intptr_t reference = (intptr_t)&object;
  printf("Base offset: %" PRIdPTR "\n", (intptr_t)(Base*)&object - reference);
  printf("a offset: %" PRIdPTR "\n", (intptr_t)&object.a - reference);
  printf("Derived offset: %" PRIdPTR "\n", (intptr_t)(Derived*)&object - reference);
  printf("b offset: %" PRIdPTR "\n", (intptr_t)&object.b - reference);
}

在我的机器 (Debian/g++) 上产生以下输出:

Base offset: 16
a offset: 16
Derived offset: 0
b offset: 8

因此,对象的布局是这样的:

+------+------+------+
| vptr |  b   |  a   |
+------+------+------+

              | Base |

|      Derived       |

如您所见,编译器没有将vptr 分配给Base 类。这是标准要求的,因为Base 是一种没有任何虚拟功能的 POD(普通旧数据)类型。 Derived 类必须包含 vptr,因为它有一个虚拟基。因此,编译器不可能将vptr-less Base 子对象放在Derived 对象的最开头,因为它需要在内存中的那个位置放置一个vptr强>。

【讨论】:

  • vptr(如果存在)不必排在第一位。它必须位于Derived 对象中的固定偏移处,以便编译器可以找到它。在过去,vptr 的位置各不相同。当微软推出 COM 时,是 COM 要求 vptr 位于最前面,而之前没有这样做的 C++ 编译器已更改,以便它们可以与 COM 一起使用。
  • @PeteBecker 我记得在(经典)Mac OS(68000 或第一代 PowerPC)上,Metrowerks CodeWarrior 在对象的一端有 vptr,在另一端有 Symantec“THINK C”/C++(我不记得哪个是哪个)。
  • @curiousguy — Borland 的编译器最初将 vtable 放在基类数据的末尾。当我们添加 COM 支持时,它移到了前面。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-04-23
  • 1970-01-01
  • 2011-03-20
  • 2020-11-18
  • 2011-08-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多