Вопрос по c++ – Размер классов в случае виртуального наследования

3

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

<code>   class A{
          char k[ 3 ];
          public:
          virtual void a(){};
          };

   class B : public  A{
          char j[ 3 ];
          public:
          virtual  void b(){};
          };

   class C : public virtual A{
          char i[ 3 ];
          public:
          virtual void c(){};
          };

   class D : public B, public C{
          char h[ 3 ];
          public:
          virtual void d(){};
          };
</code>

Вывод размера классов:

<code>    sizeof(A): 8
    sizeof(B): 12
    sizeof(C): 16
    sizeof(D): 32
</code>

Я использую компиляторgcc version 4.6.1 (Ubuntu/Linaro 4.6.1-9ubuntu3)

@ FredLarson ... я знаю о размере в случае, когда виртуальный указатель влияет на полиморфные классы ... но каков эффект виртуального наследования? Есть ли скрытый указатель, когда мы наследуем виртуально ?? Kundan Kumar
@ DumbCoder: Не все так просто. Avptr необходим для виртуальной таблицы, но вам нужны дополнительные указатели для виртуальных баз. David Rodríguez - dribeas
Помните, что все непустые базовые подобъекты должны быть правильно выровнены. Таким образом, вы можете считать, что каждый класс имеет один 4-байтовый член, выровненный по 4, если хотите. Kerrek SB
Добавьте размер vptr в свой размер класса. Для более подробной информации -> Stackoverflow.com / вопросы / 1604176 / размерный из-виртуального-указателя-с DumbCoder
Что тебя удивляет в этих размерах? Fred Larson

Ваш Ответ

4   ответа
6

3 байта в массиве, заполнение 1 байтом, 4 байта для vptr (указатель на vtable)

sizeof (B): 12

Подобъект: 8, 3 байта для дополнительного массива, заполнение 1 байтом

sizeof (C): 16

Это, наверное, неожиданно для вас ... Подобъект: 8, 3 байта для дополнительного массива, заполнение 1 байтом, 4-байтовый указатель на A

Когда у вас есть виртуальное наследование, местоположение подобъекта виртуальной базы относительно начала полного типа неизвестно, поэтому добавляется дополнительный указатель на исходный объект, чтобы отслеживать, где находится виртуальная база. Рассмотрим этот пример:

struct A {};
struct B : virtual A {};
struct C : virtual A {};
struct D : B, C {};

МестоположениеA относительно началаB объект, когда полный тип являетсяB может отличаться от местоположенияA подобъектB когда он является частью конечного объектаD. Если это не очевидно, предположим, что относительное местоположение одинаково, и проверьте, является ли относительное местоположениеA по отношению кC вC конечный объект илиC подобъект вD также можно сохранить.

Что касается последнего примера, мне не очень хочется анализировать его ... но вы можете прочитатьItanium C ++ ABI для конкретной реализации объектной модели C ++. Все остальные реализации не сильно отличаются.

Последний пример:

sizeof (D): 32

D содержит подобъект B (12) и подобъект C (16), а также дополнительный массив размера 3 и один дополнительный бит заполнения 1.

В данном конкретном случае может возникнуть вопрос: почему дваA подобъекты, еслиC наследуется практически отA, и ответ заключается в том, что виртуальная база означает, что объект готов поделиться этой базой с любым другим типом в иерархии, который также готов поделиться ею. Но в этом случаеB не хочет делиться этимA подобъект, поэтомуC нужен свой.

Вы должны быть в состоянии отследить это, добавив логи в конструкторы на разных уровнях. В случаеA пусть он принимает значение в компиляторе и передает разные значения из каждого расширяющего класса.

1

sizeof(C) больше чемsizeof(B) потому что объект типа C (потому что он наследуется практически от A) будет содержать указатель (кроме vptr, который будут также содержать объекты типа B), указывающий на ту часть себя, которую он унаследовал от A. Скотт Мейерс объясняет это подробно (около 10 страниц) в пункте 24: «Понимание стоимости виртуальных функций, множественного наследования, виртуальных базовых классов и RTTI» его книг Более эффективный C ++

-1

              Avptr Bvptr CVptr DVptr k j i h k' j' i'  TOTAL
============= ========================================= =====
sizeof(A): 8    4                     4                   8
sizeof(B): 12        4                4 4                12
sizeof(C): 16              4          4 4 4              16
sizeof(D): 32                    4    4 4 4 4  4  4  4   32

Где

vptrs занимает 4 байта каждый (64-битные указатели) символьные массивы занимают 4 байта каждый (округлены для выравнивания)k ', j' и i 'являются копиями тех переменных, которые наследуются через C, а не B.
Хорошо, хорошо. Edward Loper
C не расширяет B, поэтому он не содержит j. В дополнительных 4 есть указатель на подобъект A David Rodríguez - dribeas
0

вы можете сказать компилятору не выравнивать его в памяти с помощью #pragma pack (1). Чтобы сохранить текущие настройки упаковки и восстановить их позже, вы также можете использовать #pragma pack (push) и #pragma pack (pop).

Похожие вопросы