《C++对象模型》读后感——关于对象
c++对象模型
C++的对象模型,就是对 C++的新特性所建立的一个模型。简单点来说,它包括对象的内存布局,以及怎么用这个布局。看书上的一个例子:
1 //C style 2 typedef struct { 3 float x; 4 float y; 5 float z; 6 } Point3D; 7 8 //Point3D对象的 operations 9 Point3D Point3D_constructor(Point3D * const this, float x, float y, float z) { 10 this->x = x; 11 this->y = y; 12 this->z = z; 13 return this; 14 } 15 16 void Point3D_print(const Point3D * const pd) { 17 printf("(%g, %g, %g )", pd->x, pd->y, pd->z); 18 } 19 20 //C++ normal style 21 class Point3D { 22 public: 23 float x; 24 float y; 25 float z; 26 27 Point3D(float x, float y, float z) : x(x), y(y), z(z) {} 28 void print() const { 29 printf("(%g, %g, %g )", pd->x, pd->y, pd->z); 30 } 31 }; 32
上面的代码展示了如何在 C 和 C++中实现一个 Point3D。还没有用上继承和虚函数,就可以看出 C++要比 C 语言少写很多代码。但是 C 的代码,我们一看就知道Point3D 需要多少内存,以及内存是如何分布的。而 C++则不一定,下面将讨论3中可能的实现方式。
简单对象模型
typedef struct { float *x; float *y; float *z; (*Point3D)(Point3D * const this, float x, float y, float z); void (*print)(const Point3D * const this); } Point3D;
这个模型最为简单,把 Point3D 的内存想象为一个表,Point3D 的所有 member 都不直接存放在对象中。而是在表的每一个 slot 存放一个指针,指向其 data member 和 method member。存取 data member 和调用 method member,只需要一个表中的索引号即可。这样很容易确定对象的大小,member 数量*指针大小。但是在取得 member 时会多一层间接性,导致性能损耗,并且增大了对象的代销,不能和 C 语言很好的兼容。但是这个概念被用到了指向成员的指针(pointer-to-member)中。
表格驱动对象模型
typedef struct { struct Point3D_Data_Members *p_data[]; //数组的指针 void *p_method[]; } Point3D; struct Point3D_Data_Members { float x; float y; float z; }; void *Point3D_Method_Members[] = { &Point3D_constructor, &Point3D_print };
表格驱动的对象模型,将 data members 和 method members 指针分别放在两张表格中,而对象只需要保存这两张表的地址。这种模型,可以确定对象大小始终为两个指针的大小,但是在取得对象 members 时,仍然需要一层间接性。并且和 C 语言的结构体更不兼容了。但是这个观念被运用到了虚函数中。
C++对象模型
最后一种模型就和第一处的代码相同了。对象只保存 data members,不保存 method members。成员函数当做普通函数,在使用对象来调用函数时,由编译器调用对应的函数。这样的对象模型是兼容于 C 语言的结构体,不会带来空间的浪费(不用保存成员函数地址,由编译器来调用对应的函数),并且没有那一层间接性,效率上和 C 语言的一样。但是这仅仅是最简单的对象,对于有虚函数和虚继承的对象模型,还要额外的空间和操作来保证这些特性,这些都是编译期可见的。
- 如果类的继承体系中有虚函数(自身和基类带有虚函数),则会引入一个虚函数表指针(vfptr)。在 VC 中,通常 vfptr 放在对象开头,导致了对 C 语言的不兼容。
- 如果类的继承体系中有虚继承,则会引入一个虚基类表指针(vbptr)。在 VC 中,通常紧接在 vfptr 之后。
C/C++中的类型
这一段和 C++的对象模型没有联系。C/C++是一种强类型的语言,每一种变量都有一个明确的类型,以及此类型支持的操作,如果违反类型的操作,会被编译器报错。但是往下一层到汇编或者机器码,根本就没有 C/C++中类型的概念,只是几种长度的内存和指向某块内存的地址。那么 C/C++中的类型究竟是什么?简而言之,就是类型是编译器用来解释这块内存的标记。假如你把一块4字节的内存定义为一个 int 型,那么编译器就会把它解释为一个 int 型的数据;而把同样一块内存定义为 char[4],编译器就会把它解释为一块存有4个 char 型的内存,你只能在上面调用 char[4]的操作,而不是 int 型的操作。但是 C/C++又支持类型转换,类型转换就是告诉编译器,我想把这块内存当做另一种东西,你以后就换种方式给我解释它。