Как работает таблица виртуального наследования в g++?

Я пытаюсь лучше понять, как виртуальное наследование работает на практике (то есть не в соответствии со стандартом, а в реальной реализации, такой как 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, на что указывает печать лота A в соответствующей позиции.

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

Как видите, часть A объекта B расположена со смещением 16 байтов от начала объекта B (что могло быть другим, если бы я создал экземпляр BC и динамически преобразовал его в 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));
}

person bitmask    schedule 30.03.2012    source источник
comment
Было бы неплохо/полезно увидеть код, который делает «дамп».   -  person gbulmer    schedule 30.03.2012
comment
@gbulmer: Ну вот. Хотя я не думаю, что это сильно поможет.   -  person bitmask    schedule 30.03.2012


Ответы (1)


Ответ фактически задокументирован здесь, в ITanium ABI. В частности, раздел 2.5 содержит макет виртуального стола.

person David Rodríguez - dribeas    schedule 30.03.2012
comment
Это было мое второе предположение, но, как видно из дампа, B имеет только 16 байт (не считая части A). Первые 8 байтов — это vptr, а вторые 8 байтов — целочисленный элемент b (выровненный). По крайней мере, я так читал. Я бы ожидал смещение 16 или адрес 0x4022c8 (== 0x4022b8 + 16) где-то. Однако сейчас я посмотрю вашу ссылку. - person bitmask; 30.03.2012
comment
Описание виртуального стола (категория 3) имеет много предостережений. Знаете ли вы описание грамматики для этого? - person bitmask; 30.03.2012
comment
ссылка битая, поэтому нужно не только ссылку ставить, но и интересную часть контента ^^ - person bruno; 04.07.2020