虚拟继承不破坏静态组合?

时间:2010-12-02 13:03:47

标签: c++ virtual-inheritance

我在过去的5年里一直在假设虚拟继承打破了静态组合。

但是现在我发现,静态组合仍然保持不变,只有关于正确实例位置的其他信息。这是对的吗?

3 个答案:

答案 0 :(得分:21)

非虚拟继承中的数据布局:

class Point2d {
    int x_, y_;
};

class Point3d : public Point2d {
    int z_;
};

的Point2D:

+--------------+
| int x_       |
+--------------+
| int y_       |
+--------------+

三维点:

+--------------+   --+
| int x_       |     |
+--------------+     +-- Point2d subobject
| int y_       |     |
+--------------+   --+
| int z_       |
+--------------+

Point3d静态由Point2d和Point3d的成员组成。

在虚拟继承下

在对象内部使用偏移量变量实现。

class Point3d : public virtual Point2d {
    int z_;
};

三维点:

+-----------------+
| int z_          |
+-----------------+
| Point2d* _vbase |   --> offset to Point2d subobject (2 in this case)
+-----------------+   --+
| int x_          |     |
+-----------------+     +-- Point2d subobject
| int y_          |     |
+-----------------+   --+

在此上下文中访问Point3d* point3d->x_将转换为(C ++ Pseudocode):

(static_cast<Point2d*>(point3d) + point3d->_vbase)->x_

请注意,在vtable中有不同的方法来实现虚拟继承,例如偏移指针,这只是实现虚拟继承的一种方法。我选择了这个,因为通过vtable的间接需要更多的ascii绘图。

虚拟继承在这里没有任何好处,我希望(正如@Matthieu在评论中指出的)编译器优化这个类,以便它的内部数据布局与非虚拟继承中的相同。虚继承仅在多继承中有用(参见下面的Vertex3d类)。

在多重继承中看起来如何?

 class Vertex : virtual Point2d {
     Vertex* next_;
 };

 class Vertex3d : public Point3d, public 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

在虚拟多重继承中,基类VertexPoint3d共享Point2d中的基础Vertex3d。非虚拟的继承成员像往常一样布局。

虚拟多重继承的要点是Point3dVertex的所有后代将共享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
+------------------+   --+                     --+

参考文献:

  • Lippman:在C ++对象模型中。第3章

答案 1 :(得分:3)

使用虚拟继承的类的对象具有在编译时确定的固定内存布局。但是,访问虚拟基础需要一定程度的间接,因为你无法分辨它与派生指针的相对位置。

请参阅Wikipedia

答案 2 :(得分:0)

也许我很笨,但我不明白“静态构图”是什么意思。你说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";

}

当你说“打破静态构图”时,你的意思是当你更改界面中的pimpl时,sizeof事情会发生变化吗?