笔记-深度探索C++对象模型

第一章 关于对象

在C语言中,“数据”和“处理数据的操作(函数)”是分开来声明的,也就是说,语言本身并没有支持“数据和函数”之间的关联性。我们把这种程序方法称为程序性的(procedural),由一组“分布在各个以功能为导向的函数中”的算法所驱动,它们处理的是共同的外部数据。

如果我们声明一个struct Point3d,像这样:

1
2
3
4
5
6
typedef struct point3d
{
float x;
float y;
float z;
} Point3d;

欲打印一个Point3d,可能得定义一个像这样的函数:

1
2
3
4
void Point3d_print( const Point3d *pd )
{
printf("(%g, %g, %g)", pd->x, pd->y, pd->z);
}

在C++中,Point3d有可能采用独立的“抽象数据类型(abstract data type, ADT)来实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
class Point3d
{
public:
Point3d( float x = 0.0, float y = 0.0, float z = 0.0)
: _x(x), _y(y), _z(z) {}

void x (float xval) { _x = xval; }
// ...etc...
private:
float _x;
float _y;
float _z;
};

加上封装后的布局成本

我们问:加了封装之后,布局成本增加了多少?答案是class Point3d并没有增加成本。三个data members直接内含在每一个class object之中,就像C struct的情况一样。而member function只会诞生一个函数实例。至于每一个“拥有零个或一个定义”的inline function则会在其每一个使用者(模块)身上产生一个函数实例。Point3d支持封装性质,这一点并未给它带来任何空间或执行器的不良后果。C++在布局以及存取时间上主要的额外负担是由virtual引起的,包括:

virtual function机制,用以支持一个有效率的“执行期绑定”(runtime binding)。

virtual base class,用以实现“多次出现在继承体系中的base class,有一个单一而被共享的实例”。

一般而言,并没有什么天生的理由说C++程序一定比其C兄弟庞大或迟缓。

C++对象模型

在C++中,有两种class data members: static和nonstatic,以及三种class member functions: static、nonstatic和virtual。

在C++对象模型中,nonstatic data members被配置于每一个class object之内, static data members则被放在个别的class object之外。Static和nonstatic function members也被放在个别的class object之外,Virtual functions则以两个步骤支持之:

1.每个class产出一堆指向virtual functions的指针,放在表格之中,这个表格被称为virtual table。

2.每个class object被安插一个指针,指向相关的virtual table。vptr的setting和resetting都由每一个class的constructer、destructor和copy assigment运算符自动完成。每一个class所关联的type_info object(用以支持runtime type identification, RTTI)也经由virtual table被指出来,通常放在表格的第一个slot。

第二章 构造函数语意学

C++ Annotated Reference Manual(ARM)告诉我们:“default constructors……在需要的时候被编译器产生出来”。关键字眼是“在需要的时候”。被谁需要?答案是编译器需要。

C++ Standard说:对于class X,如果没有任何user-declared constructor,那么会有一个default constructor被隐式声明出来……一个被隐式声明出来的constructor将会是一个trivial constructor……

有4种情况,会造成“编译器必须为未声明constructor的classes合成一个default constructor”。C++ Standard把那些合成物称为implicit nontrivial default constructors。被合成出来的constructor只能满足编译器(而非程序)的需要。它之所以能够完成任务,是借着“调用member object或base class的default constructor”或是“为每一个object初始化其virtual function机制或virtual base class机制”而完成的。至于没有存在那4种情况而又没有声明任何constructor的classes,我们说它们拥有的是implicit trivial default constructors,它们实际上并不会被合成出来。

C++新手一般有两个常见的误解:

1.任何class如果没有定义default constructor,就会被合成出一个来。

2.编译器合成出来的default constructor会显示设定“class内每一个data member的默认值”。

如你所见,没有一个是真的!