Почему виртуальное наследование 2 классов увеличивает размер объекта? - PullRequest
2 голосов
/ 25 мая 2019

У меня есть простой объект, скомпилируйте и запустите под 64-битной Ubuntu1804 с g ++:

struct Base1{ int mi,mj,mk,mh;};
struct Base2{ int ni,nj,nk,nh;};

struct Child1:virtual Base1{virtual void f(){}};
struct Child2:virtual Base1{virtual void f(){}};
struct Derive1:Child1,Child2{};

struct Child3:virtual Base2{virtual void f(){}};
struct Child4:virtual Base2{virtual void f(){}};
struct Derive2:Child3,Child4{};

struct Final:Derive1,Derive2{};
int main(){
        cout<<"C1="<<sizeof(Child1)<<endl;
        cout<<"C2="<<sizeof(Child2)<<endl;
        cout<<"C3="<<sizeof(Child3)<<endl;
        cout<<"C4="<<sizeof(Child4)<<endl;
        cout<<"D1="<<sizeof(Derive1)<<endl;
        cout<<"D2="<<sizeof(Derive2)<<endl;
        cout<<"F ="<<sizeof(Final)<<endl;
        return 0;
}

Программа выводит:

$ g++ om.cpp -O2 && ./a.out
C1=24
C2=24
C3=24
C4=24
D1=32
D2=32
F =64

Я знаю, что sizeof (B1)16, и Child1-Child4 при добавлении виртуальной функции (vptr указывает на vtable) добавит дополнительный размер указателя, поэтому они имеют размер 24, без проблем.Но почему размер Derive1 / Derive2 равен 32?Объектная модель c ++ добавляет дополнительный указатель, верно?Но что на самом деле делает этот дополнительный указатель, и почему необходимо добавить этот дополнительный 8-байтовый указатель?Я не вижу здесь никакой необходимости.

Большое спасибо.

1 Ответ

1 голос
/ 25 мая 2019

Правдоподобный макет:

Final
----------------------
| Derive1
| --------------------
| | Child1
| | ------------------
| | | Pointer to Base1 (8 bytes)
| | ------------------
| | Child2
| | ------------------
| | | Pointer to Base1 (8 bytes)
| | ------------------
| --------------------
| Derive2
| --------------------
| | Child3
| | ------------------
| | | Pointer to Base2 (8 bytes)
| | ------------------
| | Child4
| | ------------------
| | | Pointer to Base2 (8 bytes)
| | ------------------
| --------------------
| Base1
| --------------------
| | mi                 (4 bytes)
| | mj                 (4 bytes)
| | mk                 (4 bytes)
| | mh                 (4 bytes)
| --------------------
| Base2
| --------------------
| | ni                 (4 bytes)
| | nj                 (4 bytes)
| | nk                 (4 bytes)
| | nh                 (4 bytes)
| --------------------
----------------------

Общий размер: 8 + 8 + 8 + 8 + 4 + 4 + 4 + 4 + 4 + 4 + 4 + 4 = 64
Обратите внимание, что этот размер может увеличиваться для размещения указателей vtable, если ваши виртуальные функции были менее тривиальными и / или фактически что-то перекрывали. (В настоящее время виртуальные функции могут быть полностью оптимизированы.)

Чтобы понять, зачем нужны все эти указатели, рассмотрим следующее:

Final foo;
Child3 * c3 = &foo;
Child4 * c4 = &foo;
Base2 * b23 = c3;
Base2 * b24 = c4;

Если бы вам дали c4, как бы вы преобразовали его в указатель на Base2? Имейте в виду, что вы не можете предполагать, что c4 указывает на часть Final; Ваше решение также должно работать для следующего, и оно должно в равной степени применяться к c3.

Child4 c4;
Base2 * b24 = &c4;
...