#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. пожалуйста, объясните мне, как работает это виртуальное наследство.
Виртуальное наследование означает, что виртуальные базовые классы существуют только один раз вместо нескольких раз. Вот почему 8 байтов из ClassA
только в ClassD
один раз. Виртуальное наследование само по себе требует определенных накладных расходов, и, следовательно, вы получаете дополнительный указатель. Точная реализация и, следовательно, точные накладные расходы не определены стандартом C ++ и могут варьироваться в зависимости от иерархии, которую вы создаете.
Когда ClassD наследует ClassB и ClassC, будет два vptr (один от B и один от C). Этот точный случай описан в статье Скотта Мейерса «Более эффективный C ++», пункт 24 (Стоимость различных языковых функций).
Виртуальные базовые классы в точности похожи на виртуальные функции: их адрес (или относительное смещение адреса) неизвестен во время компиляции:
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) оптимизация пространства для специального использования виртуальных базовых классов не кажется полезной для реальных программ.