Размер класса - C ++ - PullRequest
       20

Размер класса - C ++

1 голос
/ 13 февраля 2012

У меня есть следующий код о размерах объекта:

class A 
{ 
public: 
    int _i; 
    virtual int getI () = 0; 
    int setI (int i); 
}; 
class B : public A 
{ 
public: 
    int getI (); 
    virtual int setI (int i); 
}; 

class C : public B 
{ 
public: 
    int _i; 
    int getI (); 
    int setI (int i); 
}; 

int main () 
{ 
    B b; 
    C c; 
} 

Почему размер C c; это 12? Какие детали включены в расчет размера?

Ответы [ 5 ]

8 голосов
/ 13 февраля 2012

sizeof(int A::_i) + sizeof(int C::_i) + sizeof(pointer to virtual table)

Размер всех этих частей зависит от реализации, в вашем случае каждая имеет размер 4.

5 голосов
/ 13 февраля 2012

Размер практически любого класса зависит от реализации, но я буду предположим, что вы работаете на 32-битной машине, а класс C содержит 4 байта vptr и два четырехбайта int (A::_i и C::_i).

1 голос
/ 13 февраля 2012

Размер объекта будет зависеть от платформы.Например, на 64-битной платформе я бы ожидал, что размер будет 24 байта.То, что составляет размер объекта, немного сложно.Он состоит из множества компонентов:

  • Очевидными компонентами размера являются члены данных: в вашем случае у вас есть два int где-то в иерархии, то есть это будет способствовать 2 * sizeof(int).
  • Известный скрытый компонент - это указатель на «vtable», т. Е. Указатель на некую структуру данных, которая касается способов вызова виртуальных функций.Обычно это дает размер указателя.
  • Вероятно, большинство из них полностью игнорируют скрытые указатели с множественным наследованием, особенно когда объект включает в себя виртуальный базовый класс: чтобы объект выглядел так, как если бы он был определеннымиерархия будет включать в себя больше, чем указатель «vtable».
  • Более известным, хотя многими игнорируется, является «заполнение», то есть скрытые байты, используемые для обеспечения выравнивания элементов данных по адресам, предпочитаемым ЦП.В общем, предпочтительное выравнивание примерно соответствует размеру типа вплоть до размера строки кэша в данной системе, если существуют соответствующие фундаментальные типы этого размера.То есть, как правило, максимальное выравнивание составляет 16 байтов.
  • Наконец, я могу подумать о забавном классе с пустыми классами: поскольку подобъекты должны иметь разные адреса, каждый пустой класс будет обрабатываться так, как если бы он был как минимумодин байт большой, если только пустой класс не является базовым классом, и в этом случае он может иметь тот же адрес, что и другие базы.

Большинство скрытых вещей не применимо, но для вашего класса у вас есть 2 * sizeof(int) + sizeof(T*) для подходящего типа, используемого для доступа к таблице виртуальных функций.

0 голосов
/ 05 июня 2015

Я думаю, что вы используете 32-битный компилятор. Это макет памяти класса C:

class C size(12):
    +---
    | +--- (base class B)
    | | +--- (base class A)
 0  | | | {vfptr}
 4  | | | _i
    | | +---
    | +---
 8  | _i
    +---

C::$vftable@:
    | &C_meta
    |  0
 0  | &C::getI
 1  | &C::setI

На основании этого вы можете увидеть, какие части соответствуют размеру класса.

Вы можете узнать больше о виртуальной функции, виртуальном наследовании, виртуальной таблице, чтобы выяснить, как они организованы и соответствуют размеру класса.

Если вы используете MVSC, вы можете использовать опцию -d1reportAllClassLayout при компиляции, чтобы увидеть макет вашего класса.

0 голосов
/ 13 февраля 2012

Размер C c; на самом деле определяется реализацией.Если у вас есть какой-либо код, который на самом деле зависит от размера, этот код ужасно неправильный и может сломаться при каждом переключении компилятора.

Теперь к вашим актуальным вопросам: вы, вероятно, либо подумали, что C будет иметь размерint, содержащийся в C, или вы могли бы подумать, что он имеет размер int от C плюс значение от A.

Обе эти догадки неверны для двухпричины:

  • В ваших структурах могут быть так называемые отступы.Иногда типы должны быть приведены в соответствие с определенными границами.Для обеспечения этих выравниваний компилятор вводит некоторую область «потерянного» пространства между вашими полями, чтобы сохранить их в этих местах.Вы никогда не можете полагаться на величину этого отступа.

  • Есть еще один момент, и я, в основном, думаю, что ваш учитель хотел показать вам это: если вы пишете c.getI(), компьютер должензнать, какой метод вызывать, т. е. вызывать ли метод из A, B или C.Эту информацию нужно где-то хранить.Хранение этой информации добавляет некоторый дополнительный размер вашей структуре, но вы никогда не можете рассчитывать на то, сколько будет добавлено.Некоторые люди могут попытаться сказать вам, что это сохраняется только с помощью одного указателя, но это не правильно .Компилятору разрешено использовать столько места для хранения этой информации, сколько ему нужно.Большинство компиляторов используют только один указатель из соображений эффективности, но если вы полагаетесь на это, ваш код будет неправильным для компиляторов, которые обрабатывают это по-разному.

Если вы хотите узнать больше,просто Google для «вызова виртуального метода», и вы можете найти несколько примеров для общих реализаций для этого варианта использования.

...