C++ обычно описывает структуры классов один за другим. Позволяет проверить следующий пример кода:
#include <iostream>
#include <stdio.h>
//#define DO_PACKSTRUCTURES
//#define DEFINE_VFUNC
#ifdef DO_PACKSTRUCTURES
#pragma pack(push, 1)
#endif
class A
{
public:
A() : m_i(0) { }
#ifdef DEFINE_VFUNC
virtual
#endif
void myfunc()
{
}
protected:
int m_i;
};
class B
{
public:
B() : m_d(0.0) { }
#ifdef DEFINE_VFUNC
virtual
#endif
void myfunc2()
{
}
protected:
double m_d;
};
class C
: public A, public B
{
public:
C() : m_c('a') { }
#ifdef DEFINE_VFUNC
virtual
#endif
void myfunc3()
{
}
char m_c;
};
#ifdef DO_PACKSTRUCTURES
#pragma pack(pop)
#endif
void pprint(char* prefix, void* p)
{
printf("%s = %p\r\n", prefix, p);
}
int main()
{
C c;
A *pa = &c;
B *pb = &c;
pprint("&c", &c);
pprint("pa", pa);
printf("\r\n");
pprint("pb", pb);
pprint("pa + sizeof(A)", ((char*)pa) + sizeof(A));
printf("\r\n");
pprint("&c.m_c", &c.m_c);
pprint("pb + sizeof(B)", ((char*)pb) + sizeof(B));
printf("\r\n");
printf("sizeof(A)=%d\r\n", sizeof(A));
printf("sizeof(B)=%d\r\n", sizeof(B));
printf("sizeof(C)=%d\r\n", sizeof(C));
printf("sizeof(int)=%d\r\n", sizeof(int));
printf("sizeof(double)=%d\r\n", sizeof(double));
printf("sizeof(char)=%d\r\n", sizeof(char));
printf("sizeof(void*)=%d\r\n", sizeof(void*));
pa->myfunc();
c.myfunc2();
c.myfunc3();
return 0;
}
Win32/Debug или Release/DO_PACKSTRUCTURES & DEFINE_VFUNC неопределенны:
&c = 00BBF7A4
pa = 00BBF7A4
pb = 00BBF7AC
pa + sizeof(A) = 00BBF7A8
& c.m_c = 00BBF7B4
pb + sizeof(B) = 00BBF7B4
sizeof(A) = 4
sizeof(B) = 8
sizeof(C) = 24
sizeof(int) = 4
sizeof(double) = 8
sizeof(char) = 1
sizeof(void*) = 4
Так указатель на C * тот же указатель, как A * - потому, что А является первой базой класс, из которого начинается макет памяти. Так расположение памяти выглядит как-то так:
C*:
A
B
C members (m_c)
пб не равна (ра + SizeOf (A)) - потому что компилятор добавить некоторые байты выравнивания между А и В, чтобы ускорить доступ к B. Не уверен, насколько важно эти оптимизации - создание миллионов экземпляров одного и того же класса может повлиять на производительность.
Win32/Debug или Release/DO_PACKSTRUCTURES определяется & DEFINE_VFUNC не определено:
&c = 00B9F770
pa = 00B9F770
pb = 00B9F774
pa + sizeof(A) = 00B9F774
&c.m_c = 00B9F77C
pb + sizeof(B) = 00B9F77C
sizeof(A)=4
sizeof(B)=8
sizeof(C)=13
sizeof(int)=4
sizeof(double)=8
sizeof(char)=1
sizeof(void*)=4
Теперь мы не добавляя никаких выравнивания или байты заполнения (Из #pragma пакета (нажимной, 1)) - мы получили Класс C меньше, а также теперь pb == (pa + sizeof (A)).Теперь мы также видим, что такое «распределение» класса C, который является sizeof (int)/sizeof (double) + sizeof (char) = 4 + 8 + 1 = 13.
Win32/Debug или Release/DO_PACKSTRUCTURES & DEFINE_VFUNC определены:
&c = 007EFCF4
pa = 007EFCF4
pb = 007EFCFC
pa + sizeof(A) = 007EFCFC
&c.m_c = 007EFD08
pb + sizeof(B) = 007EFD08
sizeof(A)=8
sizeof(B)=12
sizeof(C)=21
sizeof(int)=4
sizeof(double)=8
sizeof(char)=1
sizeof(void*)=4
у нас еще есть матч расчета указатель, как и в предыдущем случае, но если рассчитать размер с помощью SizeOf (класс) - мы получим правильный размер, но не SizeOf (тип элемента) - из-за «виртуальной 'keyword - фактически ключевое слово virtual выделяет дополнительный размер указателя виртуальной таблицы.
It reflects 21 - 8 - 4 - 1 = 8
8/sizeof(void*) = 2 - that's two vtables - one from A and another from B class instances.
Я не уверен, почему сам C класс не имеет свои собственные виртуальные таблиц - для моего лучшего понимания он должен. Это можно понять позже. Компилятор Microsoft Visual C++ также имеет специальный вид ключевого слова __declspec (novtable), который также имеет некоторое представление о том, как генерируются vtable. Но что-то нормальным разработчикам не нужно, если только вы не имеете дело с расширенным программированием COM.
@SethKitchen Я думаю, вы имеете в виду, что у них одинаковый динамический тип, но разные статические типы? 'pa' - это' A' (статический), указывающий на 'C' (динамический), тогда как' pb' - это 'B' (статический), указывающий на' C' (динамический) – Kevin
. Вы можете подумать о добавлении языка -lawyer', чтобы получить стандартное объяснение, почему это происходит. – kfsone
Мораль истории: не используйте множественное наследование ни для чего, кроме микшинов. – o11c