почему sizeof (Base) не отличается от sizeof (производного) - PullRequest
4 голосов
/ 27 марта 2019

Я думаю, sizeof(Base) должно быть 12. Почему 16?

Без виртуальной функции я получаю 4 и 8.

class Base{
  public:
    int i;
    virtual void Print(){cout<<"Base Print";}
};

class Derived:public Base{
  public:
    int n;
    virtual void Print(){cout<<"Derived Print";}
};

int main(){
  Derived d;
  cout<<sizeof(Base)<<","<<sizeof(d);
  return 0;
}

ожидаемый результат: 12,16

фактический результат: 16,16

Ответы [ 2 ]

8 голосов
/ 27 марта 2019

почему 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)
1 голос
/ 27 марта 2019

Это происходит из-за того, что компилятор решил выровнять ваши классы.

Если вы хотите (или нуждаетесь), чтобы структуры или классы имели свои «реальные» размеры, вы можете использовать #pragma pack(1) следующим образом:

#pragma pack(push, 1) // Set packing to 1 byte and push old packing value to stack

class Base{
  public:
    int i;
    virtual void Print(){cout<<"Base Print";}
};

class Derived:public Base{
  public:
    int n;
    virtual void Print(){cout<<"Derived Print";}
};

int main(){
  Derived d;
  cout<<sizeof(Base)<<","<<sizeof(d);
  return 0;
}

#pragma pack(pop) // restore old packing value
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...