我在过去的5年里一直在假设虚拟继承打破了静态组合。
但是现在我发现,静态组合仍然保持不变,只有关于正确实例位置的其他信息。这是对的吗?
答案 0 :(得分:21)
class Point2d {
int x_, y_;
};
class Point3d : public Point2d {
int z_;
};
+--------------+
| 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_ | |
+-----------------+ --+
+------------------+ --+
| 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
+------------------+ --+ --+
答案 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
事情会发生变化吗?