引言
以前读《C++ Primer》的时候一直有一种感觉:该书虽然是C++入门书籍,初学者读之却觉晦涩,越往后读越是如此。等到稍加理解后再读该书,顿感醍醐灌顶,茅塞顿开。究其原因,在于原作者Stanley Lippman总是会有意无意地从编译器的角度来介绍语言的细节:对新手而言,哪里会去关注这样底层的实现呢?
当读到《Inside The C++ Object Model》时,上述感觉愈发强烈,两书之间渐进讲述的细节让人读后大呼过瘾,也深感大师级作者笔触间的睿智。
众所周知,C++是一门支持多范式的语言(《Effective C++》Item1),其中最为重要,对C语言最大的变革便是面向对象的设计思想。而本书,依其简介,探索“对象导向程序所支持的C++对象模型”下的程序行为。对于“对象导向性质之基础实现技术”以及“各种性质背后的隐含利益交换”提供一个清楚的认识。检验由程序变形所带来的效率冲击。提供对象导向观念和底层对象模型之间的效率测量。
关于对象
从C语言转化到C++时,一个显而易见的区别在于从全局数据过渡到数据封装,那么其布局成本(Layout Costs)如何?答案是并未增加。本文后续将讲述C++在布局以及存取时间上主要的额外负担是由virtual引起,包括:
此外还有一些多重继承的额外负担,除此,C++毫无理由比C慢。
C++对象模式
类的成员包括类数据成员(静态和非静态)和类成员函数(静态,非静态和虚函数)。
考虑如下简单对象模型:
我们看到,成员本身并不在对象里,只有指向对象的指针才在,如此可以避免成员因类型不同而导致存储空间不同。显然,成员以每个slot的索引值来寻址。
考虑表格驱动对象模型(见上中图1.2):
该方案中,把所有成员分离放在数据成员表和成员函数表两个表中。类对象则含有指向这两个表的指针。
遗憾的是,以上两种方案都没有真正应用于C++编译器中,但它们的设计思想,或多或少被有所继承。
来看C++对象模型的实现(见上右图1.3):
在该模型中,非静态数据成员放在类对象中,静态数据成员则放在类对象外;静态和非静态函数成员也放在类对象外;虚函数以下步骤支持:
该模型的优点在于空间和存取时间的效率,主要缺点在于非静态数据成员修改时必须重新编译。
加上继承
在虚拟继承的情况下,基类不管被派生多少次,都只有一个实体(subobject)。C++最初的继承模型不运用任何间接性:基类实体的数据成员被直接放在派生类对象中。后来又引入虚基类,则通过一些间接的基类表现方法。具体实现本文不做阐述,留待后续博文。
对象的差异
C++程序设计模型直接支持三种程序设计典范:
1.程序模型:即来自C语言的部分;
2.抽象数据类型模型:即封装与抽象;
3.面向对象模型:定义基类并派生出子类。
记住,纯粹以一种典范写程序,有利于整体行为的良好稳固。
多态
C++以下列方法支持多态:
1.隐含的转化操作:把派生类的引用/指针转化为基类的引用/指针;
2.虚函数机制:动态绑定;
3.dynamic_cast和typeid运算符。
那么,需要多少内存才能表现一个类对象呢?