размер производного класса в виртуальном наследовании

#include "stdafx.h"#include <iostream>
using namespace std;

class ClassA
{
protected:
int width, height;
public:
void set_values(int x, int y)
{
width = x;
height = y;
}
};

class ClassB : virtual public ClassA
{
//12(int + int + vptr)
};

class ClassC : virtual public ClassA
{
//12(int + int + vptr)
};

class ClassD : public ClassB, public ClassC
{
};

int main()
{
ClassA A;
ClassB B;
ClassC C;
ClassD D;
cout << "size = " << sizeof(A) << endl;
cout << "size = " << sizeof(B) << endl;
cout << "size = " << sizeof(C) << endl;
cout << "size = " << sizeof(D) << endl;
return 0;
}

вывод, который я получил:

size of ClassA = 8
size of ClassB = 12
size of ClassC = 12
size of ClassD = 16

В приведенном выше коде, почему вывод 16 для ClassD. пожалуйста, объясните мне, как работает это виртуальное наследство.

1

Решение

Виртуальное наследование означает, что виртуальные базовые классы существуют только один раз вместо нескольких раз. Вот почему 8 байтов из ClassA только в ClassD один раз. Виртуальное наследование само по себе требует определенных накладных расходов, и, следовательно, вы получаете дополнительный указатель. Точная реализация и, следовательно, точные накладные расходы не определены стандартом C ++ и могут варьироваться в зависимости от иерархии, которую вы создаете.

2

Другие решения

Когда ClassD наследует ClassB и ClassC, будет два vptr (один от B и один от C). Этот точный случай описан в статье Скотта Мейерса «Более эффективный C ++», пункт 24 (Стоимость различных языковых функций).

2

Реализации виртуальных базовых классов

Виртуальные базовые классы в точности похожи на виртуальные функции: их адрес (или относительное смещение адреса) неизвестен во время компиляции:

void f(ClassB *pb) {
ClassA *pa = pb;
}

Здесь компилятор должен вычислить смещение ClassA базовый подобъект от ClassB подобъект (или в основном производный объект). Некоторые компиляторы просто имеют указатель на него внутри ClassB; другие используют vtable, как и для виртуальных функций.

В обоих случаях накладные расходы в ClassB это один указатель.

ClassC похоже, но vptr будет указывать на ClassC Vtable, а не ClassB виртуальные таблицы.

Таким образом, ClassD объект будет содержать (это не упорядоченный список):

  • один ClassA субобъект
  • ClassB предмет
  • ClassC предмет

Так ClassD имеет два унаследованных vptr: from ClassB а также ClassC, В ClassD объект, оба vptr будут указывать на немного ClassD vtable, но такой же ClassD виртуальные таблицы:

  • ClassB субъект указывает на виртуальную таблицу ClassB-in-ClassD, которая указывает относительное положение ClassA база от ClassB база
  • ClassC субъект указывает на таблицу ClassC-in-ClassD, которая указывает относительное положение ClassA база от ClassC база

Возможная оптимизация

Я думаю, что ваш вопрос: нам нужны два разных vptr?

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

Наложение (или объединение) означает, что оба ClassB а также ClassC поделится тем же вптром: дано d экземпляр ClassD:
&d.ClassB::vptr == &d.ClassC::vptr так d.ClassB::vptr == d.ClassC::vptr но d.ClassB::vptr == &ClassC_in_ClassD_vtable а также d.ClassC::vptr == &ClassC_in_ClassD_vtable, так ClassB_in_ClassD_vtable должны быть объединены с ClassC_in_ClassD_vtable, В этом конкретном случае оба ClassB_in_ClassD_vtable а также ClassC_in_ClassD_vtable используются только для описания смещения ClassA субобъект; если ClassB а также ClassC подобъекты объединены в ClassD, то эти смещения тоже унифицированы, поэтому возможно объединение vtables.

Обратите внимание, что это возможно только здесь, так как есть полное сходство. Если ClassB а также ClassC были изменены, чтобы добавить хотя бы одну виртуальную функцию в каждую, например, эти виртуальные функции не эквивалентны (следовательно, не различимы), объединение vtable было бы невозможным.

Заключение

Эта оптимизация возможна только в очень простых случаях, подобных этому. Эти случаи не типичны для программирования на C ++: люди обычно используют виртуальные базовые классы в сочетании с виртуальными функциями. Оптимизация пустого базового класса полезна, потому что многие идиомы C ++ используют базовые классы без элементов данных или виртуальных функций. OTOH, крошечная (одна vptr) оптимизация пространства для специального использования виртуальных базовых классов не кажется полезной для реальных программ.

0
По вопросам рекламы [email protected]