Размер класса с int, функцией, виртуальной функцией в C ++? - PullRequest
15 голосов
/ 25 февраля 2012

Это тестовый вопрос на C ++, который был сделан.

#include<iostream>
using namespace std; 
class A
{

};
class B
{
int i; 
}; 

class C
{
void foo();
};
class D
{
virtual void foo();
};

class E
{
int i ; 
    virtual void foo();
};
class F
{
int i; 
    void foo();
};
class G
{
    void foo();
    int i;
    void foo1();
};

class H
{
    int i ;
    virtual void foo();
    virtual void foo1();
};
int main()
{
cout <<"sizeof(class A) : " << sizeof(A) << endl ;
cout <<"sizeof(class B) adding the member int i : " << sizeof(B) << endl ;
cout <<"sizeof(class C) adding the member void foo() : " << sizeof(C) << endl ;
cout <<"sizeof(class D) after making foo virtual : " << sizeof(D) << endl ;
cout <<"sizeof(class E) after adding foo virtual , int : " << sizeof(E) << endl ;
cout <<"sizeof(class F) after adding foo  , int : " << sizeof(F) << endl ;
cout <<"sizeof(class G) after adding foo  , int : " << sizeof(G) << endl ;
G g;
cout <<"sizeof(class G) after adding foo  , int : " << sizeof(g) << endl ;
cout <<"sizeof(class H) after adding int 2 virtual " << sizeof(H) << endl ;
return 0; 
}

выход:

sizeof(class A) : 1
sizeof(class B) adding the member int i : 4
sizeof(class C) adding the member void foo() : 1
sizeof(class D) after making foo virtual : 8
sizeof(class E) after adding foo virtual , int : 16
sizeof(class F) after adding foo  , int : 4
sizeof(class G) after adding foo   , unsigned int : 4
sizeof(class g) after adding foo  , unsigned int : 4
sizeof(class H) after adding int 2 virtual 16

Мои вопросы:

Почему siszeof(A) равно 1, а sizeof(C) тоже 1?

Почему siszeof(H) равно 16, а sizeof(G) равно 4?

Почему siszeof(E) равно 16, а sizeof(F) равно 4?

Почему siszeof(D) равно 8, а sizeof(E) равно 16?

Мое предположение:

Виртуальная функция - это указатель с 8 байтами. Но я не знаю, почему E размер равен 16? Добавление функции в пустой класс не меняет его размер?

Любая помощь приветствуется.

спасибо

Ответы [ 4 ]

37 голосов
/ 25 февраля 2012

Во-первых, виртуальная функция не является указателем с 8 байтами. В C ++ только sizeof(char) гарантированно может быть любым количеством байтов.

Во-вторых, только первая виртуальная функция в классе увеличивает свой размер (зависит от компилятора, но для большинства - если не для всех - это так). Все последующие методы нет. Невиртуальные функции не влияют на размер класса.

Это происходит потому, что экземпляр класса содержит не указатели на сами методы, а на таблицу виртуальных функций , по одному на класс.

Так что, если у вас было:

class A
{
   virtual void foo();
}

и

class B
{
   virtual void goo();
   virtual void test();
   static void m();
   void x();
}

у вас будет sizeof(A) == sizeof(B).

А теперь:

Почему siszeof (A) равно 1 и sizeof (C) тоже равно 1?

A и C имеют размер 1 только потому, что классу не разрешено иметь размер 0. Функции не имеют к этому никакого отношения. Это просто фиктивный байт.

Почему siszeof (H) равно 16, а sizeof (G) равно 4?

G имеет только один элемент, который отвечает за память - int. А на твоей платформе sizeof(int) == 4. H, кроме int, также имеет указатель на vftable (таблица виртуальных функций, см. Выше). Размер этого, размер int и выравнивание зависят от компилятора.

Почему siszeof (E) равно 16, а sizeof (F) равно 4?

Объяснено выше - не виртуальные методы не занимают память в классе.

Почему siszeof (D) равно 8, а sizeof (E) - 16?

D содержит только указатель vftable, который, по-видимому, составляет 8 байт на вашей платформе. E также имеет int, а vftable выровнен до 8 байтов. Так что это что-то вроде:

class E

4 bytes for int |  4 padding bytes  |  8 bytes for vftable pointer  | 
| x | x | x | x |    |    |    |    | v | v | v | v | v | v | v | v |
3 голосов
/ 25 февраля 2012

Почему siszeof (A) равно 1 и sizeof (C) тоже равно 1?

Функция в C не является виртуальной, поэтому классу не нужен указатель vtable, поэтому ему не нужно больше памяти, чем A. Ни A, ни C вообще не нужно никакого хранилища, но поскольку язык требует, чтобы разные экземпляры одного и того же класса имели разные указатели, они не могут иметь нулевой размер - поэтому компилятор делает их настолько маленькими, насколько это возможно, т.е. 1 байт.

Почему siszeof (H) равно 16, а sizeof (G) равно 4?

G не имеет виртуальных функций, поэтому все, что нужно для хранения, это int, который в вашем компиляторе и архитектуре занимает 4 байта.

H имеет виртуальные функции, поэтому класс должен содержать int и указатель vtable. Все широко используемые компиляторы хранят указатель vtable в начале класса, поэтому макет равен {vptr, int}, что составляет 8 + 4 = 12 байт, если вы используете 64-битный хост.

Однако компилятор может дополнить это до 16 байтов, так что если в массиве будет выделено несколько экземпляров H, то все они будут выровнены по словам. Это важно, поскольку при обращении к указателю (т. Е. К указателю vtable здесь) существуют значительные последствия для производительности, если он не выровнен по словам.

Почему siszeof (E) равно 16, а sizeof (F) равно 4?

E имеет виртуальные функции, поэтому ему нужен vtable ptr, поэтому его расположение аналогично H. У F нет виртуальных функций, у него есть только int, поэтому его макет такой же, как у G. поэтому ответ такой же, как для G и H.

Порядок членов / функций здесь не имеет значения, потому что есть только одна переменная-член, и vtable ptr всегда идет первым, если он есть.

Почему siszeof (D) равно 8, а sizeof (E) - 16?

D не имеет переменных-членов, но имеет виртуальную функцию, поэтому ему нужен указатель vtable. Указатель vtable - это единственное, что ему нужно, поэтому его размер равен sizeof(void*), что составляет 8 байт. E требуется то же самое, что и D, плюс 4 байта для целого числа, а компилятор округляет его до 16 байтов для выравнивания.

1 голос
/ 25 февраля 2012
  • SizeOf (А) == 1

Это так из-за стандарта C ++, который запрещает классы / структуры размера 0. Поэтому пустая структура / класс имеют размер 1. Я нахожу это довольно раздражающим, но у них были некоторые причины для этого.

  • SizeOf (В) == 4

Это размер int, простой и понятный:)

  • SizeOf (С) == 1

Это размер пустой структуры (см. A). Невиртуальные функции не влияют на размер объекта вообще. Вам ничего не нужно хранить в объекте, чтобы иметь возможность вызывать его не виртуальную функцию-член.

  • SizeOf (D) == 8

Я предполагаю, что вы создаете 64-битное приложение. Любой класс, имеющий хотя бы одну виртуальную функцию, имеет указатель на таблицу виртуальных методов. Это позволяет вам вызывать правильную виртуальную функцию, даже если указатель объекта был приведен к какому-либо родительскому классу. Часто указатель упоминается как vtable . Больше чтения на вики: http://en.wikipedia.org/wiki/Virtual_method_table

Я думаю, что размер 8 исходит из этого 64-битного указателя.

  • SizeOf (Е) == 16 * 1 031 *

Для хранения указателя и целого числа вам технически нужны 12 байтов. Однако указатель должен быть выровнен до 8 байтов. Теперь представьте, что вы создаете массив A объектов E. A[0].vtable будет иметь адрес & A + 0, A[0].i будет в &A+8, A[1].vtable будет в &A+12 - woops, у нас есть проблема, 12 не делится на 8. Вот почему компилятор создает padding . Он добавляет дополнительные бесполезные байты для правильного выравнивания объекта в массиве. В этом случае наименьшее число, делимое на 8, равно 16. Отсюда и размер.

  • SizeOf (F) == 4 * +1044 *

То же, что и в случае с C - не виртуальные функции вообще не влияют на размер, поэтому ваш размер соответствует B.

  • sizeof (G) == 4 - так же, как F

Количество виртуальных функций не имеет значения. В вашем объекте все еще есть только один указатель vtable. Если вы поместите больше виртуальных функций, виртуальная таблица станет больше, но не сам объект. Во многих объектно-ориентированных программах вы часто получаете множество виртуальных функций. Хранение указателей непосредственно в самом объекте было бы расточительным.

Вот почему размер (и объяснение) H соответствует размеру E.

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

Размер int с заполнением и виртуальной функцией = 18 байт

байт простой функции = 1 виртуальная функция = 8

И вы не можете просто добавить все байты, есть концепция проверки заполненияэто на Google.

Объявление в другом порядке изменить размер класса

...