Расположение данных в не виртуальном наследовании:
class Point2d {
int x_, y_;
};
class Point3d : public Point2d {
int z_;
};
Point2d:
+--------------+
| int x_ |
+--------------+
| int y_ |
+--------------+
Point3d:
+--------------+ --+
| int x_ | |
+--------------+ +-- Point2d subobject
| int y_ | |
+--------------+ --+
| int z_ |
+--------------+
Point3d статически состоит из Point2d и члена Point3d.
При виртуальном наследовании
Реализовано с помощью переменной смещения внутри объекта.
class Point3d : public virtual Point2d {
int z_;
};
Point3d:
+-----------------+
| int z_ |
+-----------------+
| Point2d* _vbase | --> offset to Point2d subobject (2 in this case)
+-----------------+ --+
| int x_ | |
+-----------------+ +-- Point2d subobject
| int y_ | |
+-----------------+ --+
Доступ к Point3d* point3d->x_
в этом контексте будет преобразован в (псевдокод C ++):
(static_cast<Point2d*>(point3d) + point3d->_vbase)->x_
Обратите внимание, что существуют разные способы реализации виртуального наследования, такие как указатели смещения внутри виртуальной таблицы, это всего лишь один из способов реализации виртуального наследования. Я выбрал этот, потому что перенаправление через vtables потребовало бы больше рисования ascii.
Виртуальное наследование здесь не имеет никакой пользы, и я ожидаю (как @Matthieu отметил в комментариях), что компилятор оптимизирует этот класс так, чтобы его внутреннее расположение данных было таким же, как в не виртуальном наследовании. Виртуальное наследование выгодно только при множественном наследовании (см. Vertex3d
класс ниже).
Как это выглядит при множественном наследовании?
class Vertex : virtual Point2d {
Vertex* next_;
};
class Vertex3d : public Point3d, public Vertex {
};
Vertex:
+-----------------+
| Vertex* next_ |
+-----------------+
| Point2d* _vbase | --> offset of Point2d subobject (2 in this case)
+-----------------+ --+
| int x_ | |
+-----------------+ +-- Point2d subobject
| int y_ | |
+-----------------+ --+
Vertex3d:
+------------------+ --+
| int z_ | |
+------------------+ +-- Point3d subobject
| Point2d* _vbase1 | |--> offset to Point2d subobject (4 in this case)
+------------------+ --+
| Vertex* next_ | |
+------------------+ +-- Vertex subobject
| Point2d* _vbase2 | |--> offset to Point2d subobject (2 in this case)
+------------------+ --+
| int x_ | |
+------------------+ +-- shared Point2d subobject
| int y_ | | both Point3d and Vertex point to this
+------------------+ --+ single copy of Point2d
В виртуальном множественном наследовании оба базовых класса Vertex
и Point3d
совместно используют базовые Point2d
в Vertex3d
. не виртуальные унаследованные члены размещаются как обычно.
Смысл виртуального множественного наследования заключается в том, что все потомки Point3d
и Vertex
будут иметь одну копию Point2d
. Без виртуального множественного наследования (= "обычного" множественного наследования) подобъект Point3d
и подобъект Vertex
Vertex3d
будут иметь собственную копию Point2d
:
Макет Vertex3d
без виртуального множественного наследования:
+------------------+ --+
| int z_ | |
+------------------+ +-- Point3d subobject --+
| int x_ | | |
+------------------+ | +-- Point2d subobject
| int y_ | | | of Point3d
+------------------+ --+ --+
| Vertex* next_ | |
+------------------+ +-- Vertex subobject --+
| int x_ | | |
+------------------+ | +-- Point2d subobject
| int y_ | | | of Vertex
+------------------+ --+ --+
Ссылки:
- Липпман: Внутри объектной модели C ++. Глава 3