一本好书:《深度探索C++对象模型》, Stanley B. Lippman 著,侯捷 译
在C语言中,“数据”和“处理数据的操作(函数)”是分开声明的,这种程序方法称为“程序性的”。C++与C在风格上和思考上都有显著的不同,C精瘦简易,C++复杂,很难说明两者孰优孰劣。
加上封装后的布局成本
C++在布局以及存取时间上主要的额外负担是由virtual引起的。包括:
- virtual function 机制 用以支持一个有效率的“执行期绑定”(runtime binding)
- vitual base class 用以实现“多次出现在继承体系中的base class”,有一个单一而被共享的实例。
此外还有一些发生在多重继承下的额外负担。然而,一般而言, 并没有天生的理由说C++程序一定比C庞大或迟缓。
1.1 C++对象模式
在C++中,有两种成员变量:static和nonstatic,三种成员函数:static,nonstatic和virtual。
1.简单对象模型
设计简单,效率低。每个成员有一个slot,按顺序声明。成员并不在object中,只是存储了指针。
2.表格驱动对象模型
变量和成员分别放在两个表中,类对象本身存储两个指针。
3.C++对象模型
非静态成员变量保存在类对象内,静态成员和函数保存在其他地方,虚函数的保存方式为:
- 每个类产生一堆指向虚函数的指针,放在表格之中,叫做虚函数表(virtual table, vtbl)。
- 每个类对象有一个指向虚函数表的指针,称为vptr。vptr的设定和重置由类的构造函数、析构函数和拷贝赋值运算符自动完成。
模型优点是空间和存取时间效率高。缺点是类改变时,使用这个类的应用程序需要重新编译。
1.2 关键词带来的差异
struct和class不仅仅是默认权限(class默认private,struct默认public)的不同,在观念和策略上也不同。我们不应该受到这样的困扰,在C中让struct保持原意即可,当需要面向对象时,使用class,避免误会吧。
1.3 对象的差异
C++程序设计模型支持三种程序设计范式(programming paradigms)。
- 程序模型(procedural model)
- 抽象数据类型模型(abstract data type model, ADT)
- 面向对象模型(object-oriented model)
纯粹使用一种典范编程,有莫大的好处,如果混杂多种典范编程有可能带来意想不到的后果,例如将继承类的对象赋值给基类对象,而妄想实现多态,便是一种ADT模型和面向对象模型混合编程带来严重后果的例子。
C++支持多态的方式:
- 经由一组隐式的转化操作。例如把一个派生类指针转化为基类指针:shape *ps = new circle();
- virtual function机制:ps->rotate();
- dynamic_cast 和typeid运算符:if ( circle *pc = dynamic_cast<c ircle* >(ps) )...
一个类对象需要的内存大小:
- 其非静态成员变量的大小总和
- 因需要内存对齐而填补的内存大小(32为机器上,通常对齐为4位)
- 为了支持virtual而产生的额外内存
一个指针所需内存大小是固定的,无论其指向何种类型。从内存观点看,指针都是一样的,没有什么不同。其差异在于多寻址出来的对象不同。指针类型会教导编译器如何解释某个特定地址中的内存内容及其大小。
关于多态的一些说明
Bear b;
ZooAnimal *pz = &b;
Bear *pb = &b;
pz和pb都指向对象b的第一个字节,但是pb涵盖的地址包含真个Bear对象,而pz涵盖的地址只包含Bear对象中ZooAnimal的子对象。除了ZooAnimal子对象中出现的成员,pz不能用来直接处理Bear的任何成员,除了virtual机制。
Bear b;
ZooAnimal za = b; //引起切割
za.rotate() //调用ZooAnimal::rotate()
编译器在za的初始化和赋值之间做了仲裁,对派生类对象做了切割以塞入较小的基类对象中,派生类对象的类型信息将被丢弃,多态不再呈现。