一、基本Class模型
在C++继承模型中,一个derived class object所表现出来的东西,是其自己的members加上其base class(es) members的总和。置于derived class members和base class(es) members的排列顺序,则未在C++ Standard中强制指定:理论上编译器可以自由安排之。在大部分编译器的上头,base class members总是先出现,但属于virtual base class 除外(一般而言,任何一条通则一旦碰到virtual base class就没辙了,这里也不例外)。了解这个模型,看下面的class声明:
class Point2d
{
public:
// constructors
// oprations
// access functions
private:
float x, y;
};
class Point3d
{
public:
// constructors
// oprations
// access functions
private:
float x, y, z;
};
在没有virtual functions的情况下(如本例),它们和C struct完全一样。这种最基本的C++class内存布局如下图所示:
二、只要继承不要多态
现让Point3d继承自Point2d,它们的声明如下:
class Point2d
{
public:
Point2d(float x = 0.0, float y = 0.0)
:_x(x), _y(y) { }
float x() { return _x; }
float y() { return _y; }
void x(float new_x) { _x = new_x; }
void y(float new_y) { _y = new_y; }
void operator+=(const Point2d &rhs)
{
_x += rhs._x;
_y += rhs._y;
}
protected:
float _x, _y;
};
class Point3d : public Point2d
{
public:
Point3d(float x = 0.0, float y = 0.0, float z = 0.0)
:Point2d(x, y), _z(z) { }
float z() { return _z; }
void z(float new_z) { _z = new_z; }
void operator+=(const Point3d &rhs)
{
Point2d::operator+=(rhs);
_z += rhs._z;
}
protected:
float _z;
};
这种单一继承而且没有virtual function时的数据布局如下图所示:
其中,sizeof(Point2d) == 8;sizeof(Point3d) == 12。
如果把一个类分解为两层或多层,有可能会为了“表现class体系之抽象化”而膨胀所需的空间。C++语言保证:“出现在derived class 中的base class subobject有其完整的原样性”。理解这些,从代码入手最简单粗暴:
class Concrete
{
public:
// ...
private:
int val;
char c1;
char c2;
char c3;
};
/* ------------ 把一个class分解为多层 ----------- */
class Concrete1
{
public:
// ...
private:
int val;
char bit1;
};
class Concrete2 : public Concrete1
{
public:
// ...
private:
char bit2;
};
class Concrete3 : public Concrete2
{
public:
// ...
private:
char bit3;
};
在一台32位或64位机器中(因为Concrete类中的char和int类型变量各自在32或64位机器中所占内存一样),每一个Concrete class object的大小都是8字节:
1、val为int类型,占4个字节;
2、char类型占1个字节。三个char类型变量各占1个字节;
3、alignment(对齐)需要1个字节。
Concrete class object内存布局如下:
下面来分析 Concrete1<—Concrete2<—Concrete3,它们的内存布局如下图所示:
这里通常会有一个疑问,为什么Concrete2中char bit2没有填补Concrete1中余下3个字节的空间?[详细讨论见P105,不再阐述]。关键是:
C++语言保证:“出现在derived class 中的base class subobject有其完整的原样性。
现在sizeof(Concrete1) == 8字节;sizeof(Concrete2) == 12字节;sizeof(Concrete3) == 16字节。
三、加上多态
四、多重继承
class Point2d
{
public:
virtual void func_Point() { cout << "Point2d"; }
protected:
float _x, _y;
};// sizeof(Point2d)为4 + 4 + 4(指针vptr所占内存的大小) = 12字节
class Point3d : public Point2d
{
public:
virtual void func_Point() { cout << "Point3d"; }
protected:
float _z;
};// sizeof(Point3d)为12 + 4(_z的大小) = 16字节
class Vertex
{
public:
virtual void func_Vertex() { cout << "Vertex"; }
protected:
Vertex* next;
};// sizeof(Vertex)为4(指针next所占内存的大小) + 4(vptr指针所占内存的大小) = 8字节
class Vertex3d : public Point3d, public Vertex
{
public:
Vertex3d(float m = 0.0) : mumble(m){ }
virtual void func_Vertex() { cout << "Vertex3d"; }
float getMumble(){ return mumble; }
protected:
float mumble;
};// sizeof(Vertex3d)为16(Point3d所占内存的大小) + 8(Vertex所占内存的大小) + 4 = 28字节
继承关系图为:
内存布局为:
五、虚拟继承
多重继承的语意上的副作用是,它必须支持某种形式的“shared subobject继承”。典型的一个例子是最早的iostream library:
class ios { ... }
class istream : public ios { ... }
class ostream : public ios { ... }
class iostream : public istream, public ostream { ... }
不论是istream还是ostream都内含有一个ios subobject。然而在iostream的对象布局中,我们只需要单一一份ios subobject就好。语言层面的解决办法就是导入虚拟继承:
class ios { ... }
class istream : public virtual ios { ... }
class ostream : public virtual ios { ... }
class iostream : public istream, public ostream { ... }
它们的继承关系图如下:
接下来的需要继续学习,updating……
参考文献:《深度探索C++对象模型》侯捷 译