почему sizeof (Base) не отличается от sizeof (производного)
Из-за выравнивания , введенного компилятором.
То есть зависит от архитектуры , но для простоты я собираюсь предположить, что мы ссылаемся на 64-битную архитектуру.
Сценарий 64 бит / Clang 8.0 .
Выравнивание типа Base
равно 8
байтов:
alignOfBase(): # @alignOfBase()
mov eax, 8
ret
Структура Base
состоит из переменной-члена (int
) и виртуальной таблицы (vtptr
).
Если мы примем «общую» архитектуру, где:
int
имеет размер 4 байта.
vtptr
- указатель. На 64-битной архитектуре имеет размер 8 байт.
У нас должна быть сумма 4 + 8 = 12
, как вы ожидаете.
Однако нам нужно помнить, что выравнивание Base
равно 8 bytes
. Следовательно, последовательные типы Base
должны храниться в месте, кратном 8.
Чтобы гарантировать это, компилятор вводит заполнение для Base
. Вот почему Base
имеет размер 16 байт.
Например, если мы рассмотрим 2 последовательных Base
(base0
и base1
) без заполнения:
0: vtptr (base 0) + 8
8: int (base 0) + 4
12: vtptr (base 1) + 8 <--- Wrong! The address 12 is not multiple of 8.
20: int (base 1) + 4
с набивкой:
0: vtptr (base 0) + 8
8: int (base 0) + 4+4 (4 padding)
16: vtptr (base 1) +8 <--- Fine! The adress 16 is multiple of 8.
24: int (base 1) +4+4 (4 padding)
Та же история для Derived
типа.
Расположение Derived
должно быть: vtptr + int + int
, то есть 8 + 4 + 4 = 16
.
Выравнивание Derived
тоже 8
:
alignOfDerived(): # @alignOfDerived()
mov eax, 8
ret
Действительно, в этом случае нет необходимости вводить заполнение, чтобы Derived
выровнялся с памятью. Размер макета будет соответствовать реальному размеру.
0: vtptr (Derived 0)
8: int (Derived 0)
12: int (Derived 0)
16: vtptr (Derived 1) <---- Fine. 16 is multiple of 8.
24: int (Derived 1)
28: int (Derived 1)