系列文章目录
第一章关于对象(Object Lessons)
第二章构造函数语意学
第三章data语意学
第四章function语意学
第五章构造、析构、拷贝语义学
第六章执行期语意学
第七章站在对象模型的尖端
第一章关于对象
在C语言中,数据和处理数据的操作(函数)是分开声明的,也就是说,语言本身并没有支持“数据和函数”之间的关联性。我们把这种方法称为程序性的,由一组“分布在各个以功能为导向的函数中”的算法所驱动,他们处理的是共同的外部数据。举个例子,如果我们声明一个struct point3d像这样:
typedef struct point3d
{
float x;
float y;
float z;
}Point3d;
想要打印一个Pint3d,肯定就得定义一个这样的函数:
void Point3d_print(const Point3d* pd)
{
printf("%g,%g,%g", pd->x, pd->y, pd->z);
}
或者要效率更好一些,就定义一个宏:
#define Point3d_printf(pd)\
printf("%g,%g,%g", pd->x, pd->y, pd->z);
亦或是直接在程序中完成操作
void my_foo()
{
Point3d* pd = get_a_point();
...
/*直接打印出point*/
printf("%g,%g,%g", pd->x, pd->y, pd->z);
}
同样的道理,某个点的特定坐标值可以直接存取
Point3d pt;
pt.x=0.0;
也可以经由一个前置处理宏来完成:
#define X(p,xval) (p.x)=(xval)
...
X(pt,0.0)
在c++中,Point3d有可能采用独立的“抽象数据类型”来实习:
class Point3d
{
public:
Point3d(float x=0.0,float y=0.0,float z=0.0):_x(x),_y(y),_z(z){}float x() { return _x; }
float y() { return _y; }
float z() { return _z; }void x(float xval) { _x = xval; }
//...etc...
private:
float _x;
float _y;
float _z;
};
或者是一个双层或者三层的class结构,更进一步来说,不管是哪一种形式,他们都可以被参数化。可以是坐标参数化也可以是坐标类型和坐标个数两者都参数化。
在c++中实现3D坐标点,比在c复杂,尤其是在使用template的情况下。但这并不意味这c++更好用。
加上封装后的布局成本
Point3d加上封装后,布局成本没有增加。c++在布局以及存取时间上主要的额外负担是由virtual(虚拟)引起的,包括
- virtual function机制 用以支持一个有效率的“执行期绑定”
- virtual base class 用以实现“多次出现在集成体关系中的base class”,有一个单一而被共享的实例。
此外还有一些多重继承下的额外负担,发生在“一个derived class和其第二或后继之base class的转换”之间。
1.1c++对象模式(the c++ Obiect Model)
在c++中,有两种类数据成员(class data members):static和nonstatic,以及三种类成员函数(class member functions):static、nonstatic和virtual。如下
class Point {
public:
Point(float xval); //构造
virtual ~Point(); //析构float x()const; //非静态
static int PoitCount(); //静态protected:
virtual ostream&; //虚拟
printf(ostream& os)const;float _x; //非静态
static int _point_count; //静态
};
简单对象模型
这个模型是为了尽量减低c++编译器的设计复杂度而开发出来的,赔上的则是空间和执行期的效率。在这个简单模型中,一个object(对象)是一系列的slots(元素),每一个slot指向一个members,Mermbers按其声明顺序,各被指定一个slot。每一个data member或function member都有自己的一个slot。
编辑过后的版本
在这个简单模型下,members本身并不放在obiect之中。只有“指向member的指针”才放在obiect内。这么做可以避免“members有不同的类型,因而需要不同的存储”所招致的问题。Obiect中的members是以slot的索引来寻址的,本例之中_x的索引是6,_point_count的索引是7,一个class object的大小很容易计算出来:“指针大小,乘以class中所声明的members个数”便是。
虽然这个模型并没有被应用于实际产品上,不过关于索引或slot个数的观念,倒是被应用到到c++的“指向成员的指针”观念之中。
表格驱动对象模型
这种对象模型是把所有与members相关的信息抽出来,放在一个data member table和一个member function table之中,class object本身则内含指向这两个表格的指针。Member function table是一系列的slots,每一个slot指出一个member function:Data member table则是直接持有data本身。
虽然这个模型也没有实际应用于真正的c++编译器上,但member function table这个观念却成为支持virtual functions的一个有效方案;
c++对象模型
当初设计的c++对象模型是从简单对象模型吧派生而来的,并对内存空间和存取时间做了优化。在此模型中,Nonstactic data members被配置于每一个class object之内,static data members则被存放在个别的class object之外。Static 和nonstatic function members 也被放在个别的class object之外。Virtual functions则以两个步骤支持之:
1.每一个class产生出一堆指向virtual functions的指针,放在表格之中。这个表格被称为virtual table(vtbl)。
2.每一个class object被安插一个指针,指向相关的virtual table。通常这个指针被称为vptr。vptr的设定和重置都由每一个class的constructor、destructor和copy assignment运算符自动完成。每一个class所关联的type_info object(用来支持runtime type identification,RTTI)也经由virtual table被指出来,通常放在表格的第一个slot。