前言
- 本文介绍的是C++的对象模型,但不是C++的类内存模型
一、C++成员种类
- 在C++中,有两种类型的数据成员:
- 在C++中,有三种类型的成员函数:
class Point {
public:
Point(float xval);
virtual ~Point();
float x()const;
static int PointCount();
protected:
virtual ostream& print(ostream &os)const;
float _x;
static int _point_count;
};
二、无继承下的C++对象模型
- 无继承下,C++对象模型的规则如下:
- 非静态数据成员存放在每一个类对象之内
- 静态数据成员存放在所有的类对象之外
- 非静态/静态成员函数都放置在所有的类对象之外
- 虚函数存放分为两个步骤:
- 1.每一个类为每一个虚函数都分别产生一个虚函数的指针,然后将这些指针放在一个虚函数表中(virtual table,vtbl)。虚函数表的第一个位置存放着所指对象的类型,用于运行时类型识别(runtime type identification,RTTI)
- 2.每一个类在内部添加一个指针(vptr),该指针指向于虚函数表
- 下面是上面Point类的对象模型:
- 优缺点:
- 优点:提高了空间和存取时间的效率
- 缺点:如果类的非静态数据成员有所修改(增加、移除、修改),那么整个应用程序需要重新编译
三、有继承下的C++对象模型
- 有继承关系下,C++对象模型的规则如下:
- 创建一个表,表中存放着指向基类的指针,然后派生类中又存放着这个表的指针
- 如果有虚继承,因为虚继承中只保存一份基类的实例,因此虚继承的子类所属的继承表中都指向同一个基类
演示案例
class istream:virtual public ios{}; //虚继承
class ostream:virtual public ios{}; //虚继承
class iostream:public istream,public ostream{};
三、对象模型如何影响程序
- 不同的对象模型,会导致“现有的程序代码必须修改”以及“必须加入新的程序代码”两个结果
演示案例
- 例如class X定义了一个拷贝构造函数、一个虚析构函数、一个虚函数foo
X foobar()
{
X xx;
X *px = new X;
xx.foo();
px->foo();
delete px;
return xx;
}
- 因此我们可以将上面的代码在内部可能会被转换为如下形式(我们会在后面的文章详细介绍每一步操作的用意):
X foobar()
{
//构造_result
//_result用来取代local xx
_result.X::X();
//扩展X *px=new X;
px = _new(sizeof(X));
if (px != 0)
px->X::X();
//扩展xx.foo()但不使用virtual机制
//以_result取代xx
foo(&_result);
//使用virtual机制扩展px->foo()
(*px->vtbl[2])(px);
//扩展delete px
if (px != 0){
(*px->vtbl[1])(px);
_delete(px);
}
//不需要named return statement
//不需要摧毁local objext xx
return;
}