问题描述
但是现在我发现,静态组合仍然保持,只有有关正确实例位置的附加信息。
非虚拟数据布局继承:
class Point2d {
int x_,y_;
};
class Point3d:public Point2d {
int z_;
};
Point2d:
+ -------------- +
| int x_ |
+ -------------- +
| int y_ |
+ -------------- +
Point3d :
+ -------------- + - +
| int x_ | |
+ -------------- + + - Point2d子对象
| int y_ | |
+ -------------- + - +
| int Z_ |
+ -------------- +
在虚拟继承下
使用偏移变量对象。
类Point3d:public virtual Point2d {
int z_;
};
Point3d:
+ ----------------- +
| int z_ |
+ ----------------- +
| Point2d * _vbase | - >偏移到Point2d子对象(在这种情况下为2)
+ ----------------- + - +
| int x_ | |
+ ----------------- + + - Point2d子对象
| int y_ | |
+ ----------------- + - +
在此上下文中访问 Point3d * point3d-> x _
将被翻译为(C ++ Pseudocode):
(static_cast< Point2d *>(point3d)+ point3d-> _vbase) - > x_
请注意,在vtable中有不同的实现虚拟继承的方法,如偏移指针,这只是实现虚拟继承的一种方法。我选择了这个,因为通过vtables的间接将需要更多的ascii绘图。
虚拟继承在这里没有好处,我期望(作为@Matthieu注释)以优化此类,使其内部数据布局与非虚拟继承中的相同。虚拟继承仅在多重继承中是有益的(见下面的 Vertex3d
类)。
继承?
class Vertex:virtual Point2d {
Vertex * next_;
};
class Vertex3d:public Point3d,public Vertex {
};
顶点:
+ ----------------- +
|顶点* next_ |
+ ----------------- +
| Point2d * _vbase | - > Point2d子对象的偏移量(在这种情况下为2)
+ ----------------- + - +
| int x_ | |
+ ----------------- + + - Point2d子对象
| int y_ | |
+ ----------------- + - +
Vertex3d:
+ ------------------ + - +
| int z_ | |
+ ------------------ + + - Point3d子对象
| Point2d * _vbase1 | | - >偏移到Point2d子对象(在这种情况下为4)
+ ------------------ + - +
|顶点* next_ | |
+ ------------------ + + - 顶点子对象
| Point2d * _vbase2 | | - >偏移到Point2d子对象(在这种情况下为2)
+ ------------------ + - +
| int x_ | |
+ ------------------ + + - 共享Point2d子对象
| int y_ | | Point3d和Vertex都指向这个
+ ------------------ + - + Point2d的单个副本
在虚拟多重继承中,两个基类顶点
和 Point3d
共享 Vertex3d
中的基础 Point2d
。
虚拟多重继承的观点是所有后代 Point3d
和顶点
将共享 Point2d
的一个副本。没有虚拟多重继承(=普通多重继承) Point3d
子对象和顶点
子对象 Vertex3d
会有自己的 Point2d
副本:
code> Vertex3d 无虚多重继承:
+ ------- ----------- + - +
| int z_ | |
+ ------------------ + + - Point3d子对象 - +
| int x_ | | |
+ ------------------ + | + - Point2d subobject
| int y_ | | | of Point3d
+ ------------------ + - + - +
|顶点* next_ | |
+ ------------------ + + - 顶点子对象 - +
| int x_ | | |
+ ------------------ + | + - Point2d subobject
| int y_ | | | of Vertex
+ ------------------ + - + - +
参考文献:
- Lippman:第3章
I was working the last 5 years with the assumption that virtual inheritance breaks static composition.
But now I discovered, that static composition is still maintained, there is just additional information about the location of the correct instance. Is this right?
Data Layout in non-virtual Inheritance:
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 is statically composed of Point2d and the member of Point3d.
Under virtual inheritance
Implemented with an offset variable inside the object.
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_ | |
+-----------------+ --+
Accessing Point3d* point3d->x_
in this context will be translated to (C++ Pseudocode):
(static_cast<Point2d*>(point3d) + point3d->_vbase)->x_
Note that there are different ways to implement virtual inheritance like offset pointers inside the vtable, this is just one way to implement virtual inheritance. I chose this one because indirection via vtables would require more ascii drawing.
Virtual inheritance has no benefit here and I would expect (as @Matthieu noted in the comments) a compiler to optimize this class so that it's internal data layout is the same as in non-virtual inheritance. Virtual inheritance is only beneficial in multiple inheritance (see Vertex3d
class below).
How does this look like in multiple inheritance?
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
In virtual multiple inheritance both base classes Vertex
and Point3d
share the base Point2d
in Vertex3d
. non-virtual inherited members are layed out as usual.
The point of virtual multiple inheritance is that all descendants of Point3d
and Vertex
will share one copy of Point2d
. Without virtual multiple inheritance (= "ordinary" multiple inheritance) both the Point3d
subobject and the Vertex
subobject of Vertex3d
would have its own copy of Point2d
:
Layout of Vertex3d
without virtual multiple inheritance:
+------------------+ --+
| int z_ | |
+------------------+ +-- Point3d subobject --+
| int x_ | | |
+------------------+ | +-- Point2d subobject
| int y_ | | | of Point3d
+------------------+ --+ --+
| Vertex* next_ | |
+------------------+ +-- Vertex subobject --+
| int x_ | | |
+------------------+ | +-- Point2d subobject
| int y_ | | | of Vertex
+------------------+ --+ --+
References:
- Lippman: Inside the C++ Object Model. Chapter 3
这篇关于虚拟继承不会打破静态组合?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!