Подтвердить что ты не робот

Sizeof класс с int, функцией, виртуальной функцией в С++?

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

#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? Добавление функции в пустой класс не изменяет ее размер?

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

спасибо

4b9b3361

Ответ 1

Во-первых, виртуальная функция не является указателем с 8 байтами. В С++ ничего, кроме 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 и allignment являются специфичными для компилятора.

Почему 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 |

Ответ 2

Почему 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 байт для выравнивания.

Ответ 3

  • SizeOf (А) == 1

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

  • SizeOf (В) == 4

То, что размер int, простой и простой:)

  • SizeOf (С) == 1

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

  • SizeOf (D) == 8

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

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

  • SizeOf (Е) == 16

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

  • SizeOf (F) == 4

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

  • sizeof (G) == 4 - то же, что F

  • SizeOf (Н) == 16

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

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

Ответ 4

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

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

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

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