i.正常情况下的布局
class Point3d {
public:
// constructor(s)
// operations
// access functions
private:
float x, y, z;
};
数据布局如图:
ii.没有多态的继承
// inheritance from concrete class
class Point3d : public Point2d {
public:
Point3d( float x = 0.0, float y = 0.0, float z = 0.0 )
: Point2d( x, y ), _z( z ) {};
protected:
float _z;
};
数据布局如图:
iii.有多态的继承
数据布局如图:
Ponit3d的vptr是使用从Ponit2d继承而来的,这时已经指向了Point3d的vbtl。如果在Point3d中重写了Point2d里的虚函数,则就是会在vbtl里替换成Point3d里的函数地址,
如果增加一个虚函数,则向vbtl增加一项,指向该函数的地址。
iv.多继承
如下代码:
protected:
float _x, _y;
};
class Vertex {
public:
virtual ~Vertex(){}
protected:
Vertex *next;
};
class Point3d : public Point2d {
public:
...
protected:
float _z;
};
class Vertex3d :
public Point3d, public Vertex {
public:
...
protected:
float mumble;
};
数据布局如图:
对于一个多重派生类对象,将其地址指定给“最左端(也就是第一个)base class的指针”,
情况将和单一继承时相同,因为两者都指向相同的起始地址,需付出的成本只有地址的操作而已。至于第二个或第三个后继的base class的地址指定的操作,
则需要将地址修改:加上(或减去,如果downcast的话)介于中间的base class subject(s)大小。例如:
Vertex3d v3d;
Vertex* pv;
Pv = &v3d
在内部会转化为
Pv = (Vertex*)(((char*)&v3d) + sizeof(Point3d));
iv.虚拟继承
class Vertex : public virtual Point2d {
public:
protected:
Vertex *next;
};
class Point3d : public virtual Point2d {
public:
protected:
float _z;
};
class Vertex3d :
public Point3d, public Vertex {
public:
protected:
float mumble;
};
数据分布如图:
上述的四个类的大小分别为:12,20,20,32.(vs2008测试)
如果把Point2d的析构函数非虚,四个类的大小分别为:8,16,16,28.
这是因为Point2d,point3d,Vertex都没有vbtl,在它们的对象中也就没有了vptr.
虚继承实际继承链中只有一个实体,继承类对象中包含指向这个唯一实体的指针。