Вопрос о множественном наследовании, виртуальных базовых классах и размере объекта в C ++

Следующий код печатает 20, т.е. sizeof (z) равен 20.

#include <iostream.h>
class Base
{
      public:
            int a;
};

class X:virtual public Base
{
      public:
            int x;
};

class Y:virtual public Base
{
      public:
            int y;
};

class Z:public X,public Y
{
};

int main()
{
Z z;
cout << sizeof(z) <<endl;
}

Тогда как, если я не использую здесь виртуальные базовые классы, то есть для следующего кода: sizeof (z) равно 16.

#include <iostream.h>
class Base
{
      public:
            int a;
};

class X:public Base
{
      public:
            int x;
};

class Y:public Base
{
      public:
            int y;
};

class Z:public X,public Y
{
};

int main()
{
Z z;
cout << sizeof(z) <<endl;
}

Почему sizeof (z) больше (20) в первом случае? Разве это не должно быть 12, так как Base будет включен в Z только один раз?


person simplfuzz    schedule 28.12.2008    source источник


Ответы (3)


Давайте посмотрим на расположение классов в двух случаях.

Без виртуального у вас есть два базовых класса («X» и «Y») с целым числом каждый, и каждый из этих классов интегрировал в себя базовый класс «Base», который также имеет целое число. Это 4 целых числа по 32 бита каждое, всего 16 байт.

Offset  Size  Type  Scope  Name
     0     4   int   Base     a
     4     4   int      X     x
     8     4   int   Base     a
    12     4   int      Y     y
    16 size (Z members would come at the end)

(Изменить: я написал программу на DJGPP, чтобы получить макет и настроил таблицу, чтобы учесть это.)

Теперь поговорим о виртуальных базовых классах: они заменяют фактический экземпляр класса указателем на общий экземпляр. Ваш класс «Z» имеет только один «базовый» класс, и оба экземпляра «X» и «Y» указывают на него. Следовательно, у вас есть целые числа в X, Y и Z, но у вас есть только одно Z. Это означает, что у вас есть три целых числа или 12 байтов. Но у X и Y также есть указатель на общий Z (иначе они не знали бы, где его найти). На 32-битной машине два указателя добавят дополнительные 8 байтов. Это всего 20, которые вы видите. Схема памяти может выглядеть примерно так (я не проверял это ... у ARM есть пример, где порядок - X, Y, Z, затем Base):

Offset  Size        Type  Scope  Name  Value (sort of)
     0     4 Base offset      X     ?  16 (or ptr to vtable)
     4     4         int      X     x
     8     4 Base offset      Y     ?  16 (or ptr to vtable)
    12     4         int      Y     y
    16     4         int   Base     a
    20 size (Z members would come before the Base)

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

Изменить: ppinsider предоставил дополнительную информацию о случае gcc, в котором он демонстрирует, что gcc реализует указатель на виртуальный базовый класс, используя в противном случае пустую vtable (то есть без виртуальных функций). Таким образом, если бы были виртуальные функции, не потребовался бы дополнительный указатель в экземпляре класса, требующий больше памяти. Я подозреваю, что обратная сторона - это дополнительное косвенное обращение к базовому классу.

Мы могли бы ожидать, что все компиляторы сделают это, но, возможно, нет. На странице 225 ARM обсуждаются виртуальные базовые классы без упоминания vtables. Страница 235 специально обращается к «виртуальным базовым классам с виртуальными функциями» и имеет диаграмму, показывающую структуру памяти, где есть указатели из частей X и Y, которые отделены от указателей на vtable. Я бы посоветовал никому не считать само собой разумеющимся, что указатель на Base будет реализован в виде таблицы.

person markets    schedule 28.12.2008
comment
Марк, как вы создали этот макет? - person Charlie Martin; 28.12.2008
comment
Если вы имеете в виду формат таблицы, то это просто код. Я только догадался о содержании. Я отредактирую свой ответ с более подробной информацией и ссылкой на ответ ppinsider. - person markets; 31.12.2008

Ответ Марка Сантессона в значительной степени зависит от денег, но утверждение о том, что vtables нет, неверно. Вы можете использовать g ++ -fdump-class -ierarchy, чтобы показать, что происходит. Вот случай отсутствия виртуалов:

Class Base
   size=4 align=4
   base size=4 base align=4
Base (0x19a8400) 0

Class X
   size=8 align=4
   base size=8 base align=4
X (0x19a8440) 0
  Base (0x19a8480) 0

Class Y
   size=8 align=4
   base size=8 base align=4
Y (0x19a84c0) 0
  Base (0x19a8500) 0

Class Z
   size=16 align=4
   base size=16 base align=4
Z (0x19b1800) 0
  X (0x19a8540) 0
    Base (0x19a8580) 0
  Y (0x19a85c0) 8
    Base (0x19a8600) 8

Обратите особое внимание на аргумент «базовый размер». Теперь случай виртуальных машин, показывающий только Z:

Class Z
   size=20 align=4
   base size=16 base align=4
Z (0x19b3000) 0
    vptridx=0u vptr=((& Z::_ZTV1Z) + 12u)
  X (0x19a8840) 0
      primary-for Z (0x19b3000)
      subvttidx=4u
    Base (0x19a8880) 16 virtual
        vbaseoffset=-0x0000000000000000c
  Y (0x19a88c0) 8
      subvttidx=8u vptridx=12u vptr=((& Z::_ZTV1Z) + 24u)
    Base (0x19a8880) alternative-path

Обратите внимание, что «базовый размер» такой же, но «размер» на один указатель больше, и обратите внимание, что теперь есть указатель vtable! Это, в свою очередь, содержит таблицы построения для родительских классов и всю межклассовую магию (таблицы построения и таблицу виртуальных таблиц (VTT)), как описано здесь:

http://www.cse.wustl.edu/~mdeters/seminar/fall2005/mi.html

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

person user23167    schedule 28.12.2008
comment
но если ни один из классов не имеет виртуальной функции, почему именно vtable? - person wilhelmtell; 29.12.2008
comment
Запустите код через g ++ с этой опцией, и вы поймете, почему. Посмотрите ссылку, которую я дал, чтобы понять все; намного лучше, чем я пытаюсь повторно хешировать его здесь! - person user23167; 29.12.2008
comment
что означает base в base size = 16 base align = 4? Означает ли это все базовые классы? - person ibread; 07.01.2010

Дополнительный размер, вероятно, связан с дополнительными VTables (http://en.wikipedia.org/wiki/Vtable) выделены виртуальными классами и множественным наследованием.

person Ana Betts    schedule 28.12.2008