【问题标题】:How does the virtual inheritance table work in g++?虚拟继承表在 g++ 中是如何工作的?
【发布时间】:2012-03-30 17:36:20
【问题描述】:

我试图更好地理解虚拟继承在实践中是如何工作的(也就是说,不是根据标准,而是在像g++ 这样的实际实现中)。实际问题在底部,以粗体显示。

因此,我为自己构建了一个继承图,其中包含以下简单类型:

struct A {
  unsigned a;
  unsigned long long u;
  A() : a(0xAAAAAAAA), u(0x1111111111111111ull) {}
  virtual ~A() {}
};

struct B : virtual A {
  unsigned b;
  B() : b(0xBBBBBBBB) {
    a = 0xABABABAB;
  }
};

(在整个层次结构中,我还有一个C: virtual A 和BC: B,C,因此虚拟继承是有意义的。)

我编写了几个函数来转储实例的布局,获取 vtable 指针并打印前 6 个 8 字节值(任意以适合屏幕),然后转储对象的实际内存。这看起来像这样:

转储A 对象:

actual A object of size 24 at location 0x936010
vtable expected at 0x402310 {
          401036,          401068,          434232,               0,               0,               0,
}
1023400000000000aaaaaaaa000000001111111111111111
[--vtable ptr--]

转储一个B对象以及A对象所在的位置,通过在相应位置打印很多As来表示。

actual B object of size 40 at location 0x936030
vtable expected at 0x4022b8 {
          4012d2,          40133c,        fffffff0,        fffffff0,          4023c0,          4012c8,
}
b822400000000000bbbbbbbb00000000e022400000000000abababab000000001111111111111111
                                AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA  (offset: 16)

如您所见,B 的 A 部分位于 16 字节到 B 对象开头的偏移处(如果我实例化了 BC 和 dyn 可能会有所不同- 将其投射到 B*!)。

我本来希望16(或至少一个2,由于对齐)出现在表格中的某处,因为程序必须查找实际位置(运行时A 的偏移量)。 那么,布局到底是什么样子的?


编辑:转储是通过调用 dump 和 dumpPositions 完成的:

using std::cout;
using std::endl;

template <typename FROM, typename TO, typename STR> void dumpPositions(FROM const* x, STR name) {
  uintptr_t const offset {reinterpret_cast<uintptr_t>(dynamic_cast<TO const*>(x)) - reinterpret_cast<uintptr_t>(x)};
  for (unsigned i = 0; i < sizeof(FROM); i++) {
    if (offset <= i && i < offset+sizeof(TO))
      cout << name << name;
    else
      cout << "  ";
  }
  cout << "  (offset: " << offset << ")";
  cout << endl;
}
template <typename T> void hexDump(T const* x, size_t const length, bool const comma = false) {
  for (unsigned i = 0; i < length; i++) {
    T const& value {static_cast<T const&>(x[i])};
    cout.width(sizeof(T)*2);
    if (sizeof(T) > 1)
      cout.fill(' ');
    else
      cout.fill('0');
    cout << std::hex << std::right << (unsigned)value << std::dec;
    if (comma)
      cout << ",";
  }
  cout << endl;
}
template <typename FROM, typename STR> void dump(FROM const* x, STR name) {
  cout << name << " object of size " << sizeof(FROM) << " at location " << x << endl;
  uintptr_t const* const* const vtable {reinterpret_cast<uintptr_t const* const*>(x)};
  cout << "vtable expected at " << reinterpret_cast<void const*>(*vtable) << " {" << endl;
  hexDump(*vtable,6,true);
  cout << "}" << endl;
  hexDump(reinterpret_cast<unsigned char const*>(x),sizeof(FROM));
}

【问题讨论】:

  • 看看执行“转储”的代码会很好/很有帮助。
  • @gbulmer:你去吧。虽然我认为它没有多大帮助。

标签: c++ g++ internals vtable virtual-inheritance


【解决方案1】:

答案实际上记录在这里,在Itanium ABI。特别是第 2.5 节包含虚拟表的布局。

【讨论】:

  • 这是我的第二个猜测,但正如您在转储中看到的那样,B 只有 16 个字节(除了A 部分)。前 8 个字节是 vptr,而后 8 个字节是 b 整数成员(对齐)。至少,我是这么读的。我希望偏移量 16 或地址 0x4022c8 (== 0x4022b8 + 16) 某处。不过,我现在要看看你的链接。
  • 虚拟表描述(3类)有很多注意事项。你知道这个的语法描述吗?
  • 链接坏了,所以不仅要放链接,还要放内容的有趣部分^^
猜你喜欢
  • 2023-03-23
  • 2012-08-31
  • 1970-01-01
  • 2016-10-26
  • 2013-08-24
  • 1970-01-01
  • 2011-08-20
相关资源
最近更新 更多