Я пытаюсь понять расположение объектов для C ++ в множественном наследовании.
Для этого у меня есть два суперкласса A, B и один подкласс C.
То, что я ожидал увидеть при попытке сброса, было:
vfptr | поля А | vfptr | поля B | поля С.
Я получаю эту модель, но с некоторыми нулями, которые я не понимаю.
Вот код, который я пытаюсь
#include <iostream>
using namespace std;
class A{
public:
int a;
A(){ a = 5; }
virtual void foo(){ }
};
class B{
public:
int b;
B(){ b = 10; }
virtual void foo2(){ }
};
class C : public A, public B{
public:
int c;
C(){ c = 15; a = 20;}
virtual void foo2(){ cout << "Heeello!\n";}
};
int main()
{
C c;
int *ptr;
ptr = (int *)&c;
cout << *ptr << endl;
ptr++;
cout << *ptr << endl;
ptr++;
cout << *ptr << endl;
ptr++;
cout << *ptr << endl;
ptr++;
cout << *ptr << endl;
ptr++;
cout << *ptr << endl;
ptr++;
cout << *ptr << endl;
ptr++;
cout << *ptr << endl;
ptr++;
cout << *ptr << endl;
return 0;
}
И вот результат, который я получаю:
4198384 //vfptr
0
20 // value of a
0
4198416 //vfptr
0
10 // value of b
15 // value of c
В чем смысл нулей между ними?
Заранее спасибо!
Это зависит от вашего компилятора. С clang-500 я получаю:
191787296
1
20
0
191787328
1
10
15
1785512560
Я уверен, что есть способ GDB, но это то, что я получу, если я дам дамп слов размером с указатель с LLDB по адресу объекта класса:
0x7fff5fbff9d0: 0x0000000100002120 vtable for C + 16
0x7fff5fbff9d8: 0x0000000000000014
0x7fff5fbff9e0: 0x0000000100002140 vtable for C + 48
0x7fff5fbff9e8: 0x0000000f0000000a
Это расположение кажется разумным, верно? Как раз то, что вы ожидаете.
Причина того, что в вашей программе это выглядит не так чисто, как в отладчике, заключается в том, что вы сбрасываете слова размера int. В 64-битной системе sizeof (int) == 4, но sizeof (void *) == 8
Итак, вы видите, что ваши указатели разбиты на (int, int) пары. В Linux ваши указатели не имеют битов, установленных ниже 32, в OSX мои указатели делают — следовательно, причина дисбаланса 0 против 1
это в значительной степени зависит от архитектуры и компилятора … Возможно, для вас размер указателя может не соответствовать размеру int … Какую архитектуру / компилятор вы используете?
Если вы работаете в 64-битной системе, то:
Первый ноль — это 4 старших байта первого vfptr
,
Второй ноль является дополнением, так что второй vfptr
будет выровнен по 8-байтовому адресу.
Третий ноль — 4 старших байта второго vfptr
,
Вы можете проверить, если sizeof(void*) == 8
чтобы утверждать это.
Трудно сказать, не зная вашей платформы и компилятора, но это может быть проблемой выравнивания. По сути, компилятор может попытаться выровнять данные класса по 8-байтовым границам с нулями, используемыми для заполнения.
Без вышеперечисленных деталей это всего лишь домыслы.
Это полностью зависит от вашего компилятора, системы, битности.
Указатель виртуальной таблицы будет иметь размер указателя. Это зависит от того, компилируете ли вы файл как 32-битный или 64-битный. Указатели также будут выровнены по нескольким адресам их размера (как обычно бывает у любого типа). Вероятно, поэтому вы видите заполнение 0 после 20
,
Целые числа будут иметь размер целого числа в вашей конкретной системе. Обычно это всегда 32-битный. Обратите внимание, что если это не так на вашем компьютере, вы получите неожиданные результаты, потому что вы увеличиваете свой ptr на sizeof (int) с помощью арифметики с указателями.
Если вы используете MVSC, вы можете сбросить всю структуру памяти всех классов в вашем решении с -d1reportAllClassLayout следующим образом:
cl -d1reportAllClassLayout main.cpp
Надеюсь, что это полезно для вас