Я играю с опцией компилятора fdump-class-иерархии, но я не знаю, как я могу понять вывод. Что означают «размер», «выравнивание», «базовый размер» и «выравнивание базы» и как они учитываются? Спасибо!
Когда код:
class A
{
public:
private:
double m_nothing;
int m_number;
};
Выход:
Class A
size=16 align=8
base size=16 base align=8
A (0x406c690) 0
Но, если я немного изменю класс:
class A
{
public:
private:
int m_number;
double m_nothing;
};
вывод будет:
Class A
size=16 align=8
base size=12 base align=8
A (0x406c690) 0
size
а также align
Размер и выравнивание класса, когда используется как полный тип. То есть, если вы создаете объекты, полный тип которых является этим типом (например, определение переменных этого типа или использование этого типа с new
).
Размер — это просто количество байтов, которое он занимает. Так size=16
означает, что при использовании в качестве завершенного типа он всегда занимает 16 байтов.
Выравнивание говорит вам, где объект может быть размещен: align=8
означает, что адрес объекта должен быть целым, кратным 8.
base size
а также base align
дать размер и выравнивание в случае, если класс используется в качестве базового класса. Причина, по которой они различаются, заключается в том, что стандарт C ++ позволяет объектам использовать меньше отступов при использовании в качестве базового класса.
Итак, давайте посмотрим конкретно на ваш пример (я предполагаю, что у вас есть int
перед double
в первом случае). Я также опускаю public
а также private
потому что здесь они ничего не меняют (если у вас были как открытые, так и частные члены данных, они мог в принципе что-то изменить, но я не знаю, пользуется ли какой-либо компилятор этим преимуществом). Я также угадываю размер и выравнивание int
а также double
(на самом деле значения, которые я предполагаю, довольно распространенный выбор, и объясняют значения, которые вы получаете).
Так что в первом случае (я предполагаю) у вас есть
class A
{
int m_number;
double m_nothing;
};
Сейчас int
имеет размер и выравнивание 4
и двойной имеет размер и выравнивание 8
,
Итак, давайте выполним работу компилятора и создадим наш класс.
Во-первых, у нас есть m_number
, который занимает 4 байта. Мы должны поместить членов в указанном порядке, поэтому m_number
идет в начале A
:
iiii
До сих пор у нас есть размер 4 (четыре байта для int) и выравнивание 4 (потому что int имеет выравнивание 4). Но теперь мы должны добавить двойной (размер и выравнивание 8). Так как непосредственно после int мы находимся по (относительному) адресу 4, мы не правильно выровнены по двойному, поэтому мы должны добавить 4 набивка байты (которые я отмечу *
), чтобы получить кратное 8. Таким образом, мы получаем для нашего класса:
iiii****dddddddd
Теперь, если класс используется в качестве базового класса, мы закончили. Таким образом, мы имеем base size=16
а также base align=8
(нам нужно выравнивание 8, чтобы правильно выровнять двойник).
Для законченного объекта есть еще одно соображение: стандарт требует, чтобы в массивах объекты следовали друг за другом без промежутка между ними. То есть первый байт после объекта должен быть правильно выровнен для следующего объекта. Что в конечном итоге означает, что размер всего объекта должен быть кратным его выравниванию.
Теперь найденный нами макет объекта уже удовлетворяет этому требованию. Поэтому мы можем использовать его без изменений и для всего объекта. Поэтому мы получаем size=16
а также align=8
для полного объекта.
Теперь рассмотрим случай, когда заказ полностью изменен:
class A
{
double m_nothing;
int m_number;
};
Теперь мы должны начать с double
:
dddddddd
Далее мы должны добавить int
, Оказывается, следующее свободное место уже правильно выровнено для int
поэтому мы можем просто добавить его:
ddddddddiiii
Теперь для использования в качестве базового объекта мы готовы. Как видите, нам потребовалось всего 12 байтов, поэтому base size=12
, Конечно для double
чтобы быть правильно выровненным, объект снова должен начинаться с адреса, кратного 8. Поэтому мы имеем base align=8
,
Однако для иска как завершенного объекта, мы теперь находим, что следующий адрес будет в позиции 12, которая не правильно выровнен для double
член. Поэтому мы должны добавлять байты заполнения до тех пор, пока не достигнем правильно выровненного адреса:
ddddddddiiii****
Как видите, теперь нам нужно 16 байтов, таким образом size=16
, У нас все еще есть align=8
из-за двойника.
Обратите внимание, что требование выравнивания может существенно повлиять на размер класса. Рассмотрим, например, следующие два типа:
struct S1
{
char c1;
double d1;
char c2;
double d2;
char c3;
};
struct S2
{
double d1;
double d2;
char c1;
char c2;
char c3;
};
Хотя оба содержат одинаковых членов, S1
будет с размерами и выравниваниями выше иметь общий (не базовый) размер 40, в то время как общий размер S2
будет всего 24. Действительно, объекты типа S1
будет, как законченный объект, выглядеть
c*******ddddddddc*******ddddddddc*******
в то время как те, типа S2
будет выглядеть
ddddddddddddddddccc*****
Итак, суть в том, что члены с самым высоким требованием выравнивания всегда должны стоять на первом месте.
Также обратите внимание, что sizeof
возвращает размер завершенных объектов, то есть то, что вызывает дамп иерархии классов size
,
Других решений пока нет …