Виртуальное наследование не нарушает статическую композицию? - PullRequest
5 голосов
/ 02 декабря 2010

Я работал последние 5 лет с предположением, что виртуальное наследование нарушает статическую композицию.

Но теперь я обнаружил, что статическая композиция все еще поддерживается, есть только дополнительная информация о местонахождении правильного экземпляра. Это правильно?

Ответы [ 3 ]

21 голосов
/ 02 декабря 2010

Расположение данных в не виртуальном наследовании:

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
3 голосов
/ 02 декабря 2010

Объекты классов, которые используют виртуальное наследование, имеют фиксированную структуру памяти, которая определяется во время компиляции. Однако для доступа к виртуальной базе необходим уровень косвенности, поскольку вы не можете сказать, где она находится относительно производного указателя.

См. Википедия

0 голосов
/ 02 декабря 2010

Может быть, я тупой, но я не понимаю, что вы подразумеваете под «статической композицией».Вы говорите, что pimpl нарушает его, поэтому давайте начнем с этого и возьмем из него полиморфизм и виртуальное наследование.

Предположим, у вас есть этот код:

#include <iostream>
using namespace std;

class Implementation
{
public:
    bool do_foo() { return true; }
};

class Implementation2
{
public:
    bool do_foo() { return false; }
private:
    char buffer_[1024];
};

class Interface
{
public:
    Interface(void* impl) : impl_(impl) {};
    bool foo() { return reinterpret_cast<Implementation*>(impl_)->do_foo(); }
    void change_impl(void* new_impl) { impl_ = new_impl; }

private:
    void* impl_;
};

int main()
{
    Implementation impl1;
    Implementation2 impl2;

    Interface ifc(&impl1);
    cout << "sizeof(ifc+impl1) =  " << sizeof(ifc) << "\n";

    Interface ifc2(&impl2);
    cout << "sizeof(ifc2+impl2) =  " << sizeof(ifc2) << "\n";

    ifc.change_impl(&impl2);
    cout << "sizeof(ifc+impl2) =  " << sizeof(ifc) << "\n";

    cout << "sizeof(impl) = " << sizeof(impl1) << "\n";
    cout << "sizeof(impl2) = " << sizeof(impl2) << "\n";

}

Когда вы говорите "нарушает статическую композицию"Вы имеете в виду sizeof вещи меняются при изменении pimpl в интерфейсе?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...