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?
Добавление функции в пустой класс не изменяет ее размер?
Любая помощь приветствуется.
спасибо
Ответы
Ответ 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
Это из-за стандарта С++, который запрещает классы/структуры размером 0. Вот почему пустая структура/класс имеет размер 1.
Я нахожу это довольно раздражающим, но у них есть некоторые соображения для этого.
То, что размер int, простой и простой:)
Это размер пустой структуры (см. A). Невиртуальные функции вообще не влияют на размер объекта. Вам нечего хранить в объекте, чтобы иметь возможность вызвать его невиртуальную функцию-член.
Я предполагаю, что вы создаете 64-битное приложение. Любой класс, имеющий по крайней мере 1 виртуальную функцию, имеет указатель на таблицу виртуальных методов. Это позволяет вам вызывать правильную виртуальную функцию, даже если указатель объекта был отброшен в какой-либо родительский класс. Часто указатель называется vtable. Подробнее читайте на wiki: http://en.wikipedia.org/wiki/Virtual_method_table
Я думаю, что размер 8 исходит от этого 64-битного указателя.
Чтобы сохранить указатель и 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. Следовательно, размер.
То же, что и в случае C - не виртуальные функции не вносят никакого вклада в размер, поэтому ваш размер соответствует B.
Число виртуальных функций не имеет значения. В вашем объекте все еще есть только один указатель vtable. Если вы ставите больше виртуальных функций, виртуальная таблица становится больше, но не сама объект. Во многих объектно-ориентированных программах вы часто получаете множество виртуальных функций. Наличие указателей, хранящихся непосредственно в самом объекте, будет расточительным.
Вот почему размер (и объяснение) H соответствует размеру E.
Ответ 4
Размер int с заполнением и виртуальной функцией = 18 байт
простая функция bytes = 1
виртуальная функция = 8
И вы не можете просто добавить все байты, есть концепция дополнения, чтобы проверить это на google.
Объявление в другом порядке изменяет размер класса