在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;
};
这个Point类在机器中将会怎么表示呢?
单对象模型
优点:
- 降低了C++编译器的设计复杂度
缺点:
- 空间和执行效率低
实现:
- 一个对象就是一个slots,每一个slot指向一个成员。
- 成员按照其声明次序,各被指定一个slot
- 每一个数据成员和对象成员都有自己的slot
- 成员本身并不放在对象中,只有“指向成员的指针”才放在对象内。这样做可以避免“成员有不同的类型,因而需要不同的存储空间”导致的问题
- 对象中的成员是以slot的索引值来寻找。上面:_x的索引是6,_point_count的索引是7.
- 一个类对象的大小 = (指针大小 * class中所声明的成员数目)
表格驱动对象模型
实现:
- 把所有成员的信息抽出来,放在数据成员表和成员函数表中,类对象本身则含有一个指向这两个表格的指针
- 成员函数表是一系列的slots,每一个slot指向一个成员函数。
- 数据成员表直接含有数据本身
C++对象模型
优点:
- 空间和存取效率高
缺点:
- 如果应用程序代码本身未曾改变,但所用到的类对象的非静态数据成员有所修改(增加、移除会在更改),那么应用程序代码需要重写编译
实现: - 非静态数据成员被配置于每一个类对象中
- 静态数据成员被存在在所有类对象之外
- 静态成员函数和非静态成员函数被放在所有的类对象之外
- 虚函数分两个步骤:
-
每一个类产生出一堆指向虚函数的指针,放在表格中,这个表格叫做虚函数表(virtual table,vtbl)
-
每一个类对象被添加了一个指针
vptr
,指向相关的虚函数表。vptr
的设置和重置都由每一个类的构造、析构、拷贝运算符自动完成- 每一个类所关联的
type objetc
(用以支持runtime type identification,RTTI)也由虚函数表指出来,通常是放在表格的第一个slot处。
-
继承与对象模型
C++支持单一继承:
class Library_material{};
class Book : public Library_material{};
class Rental_book : public Book {};
C++支持多重继承:
class iostream : public istream, public ostream{...};
C++支持虚拟继承(virtual,也就是共享的意思)
class istream : virtual public ios {};
class ostream : virtual public ios {};
在虚拟继承的情况下,基类不管在继承串链中被派生多少次,永远只会存在一个实体(subobject[基类子对象])
一个派生类是如果塑造基类的实体呢?
在简单对象模型中:
-
每一个基类可以被派生类对象内的一个slot指出,也就是说,该slot中存的是base class subobject的地址
-
缺点:因为间接性导致空间和存取时间上的额外负担
-
优点:类对象的大小不会因为基类的改变而被影响
另一个基类模型:
-
生成基类表时,表格中的每一个slot包含一个相关的基类(base class)地址
-
每一个类对象内含一个bptr,被初始化时指向其基类表
-
优点:因为间接性导致空间和存取时间上的额外负担
-
缺点:
- 每一个类对象中对于继承都有一致的表现方式:每一个类对象都应该在某个固定位置上放置一个基类表指针,与base classes的大小和数据无关
- 不需要改变类对象本身,就可以放大、缩小会在更改基类表
但是,上面两种方法,其间接性都将因继承的深度而增加,或者需要额外的空间来放置额外的指针
C++最初采用的继承模型并没有任何间接性:基类子对象(base class subobject)的数据成员被直接放置在派生类对象中。
- 优点:提供了对基类对象最有效期的提取
- 缺点:基类成员的任何改变(包括增加、移除、改变类型等),都需要重写编译所有用到这个基类或者派生类的对象
C++2.0引入了虚基类(vritual base class),需要一些间接的基类表现方法:
- 虚基类的原始模型是在类对象中为每一个有关联的虚基类加上一个指针
- 其他演化出来的模型要么是导入一个虚基类表(vritual base class table),要么是扩充已经存在的虚函数表(vritual table),以维护每一个虚基类的位置