基本信息
- 原书名:Inside the C++ Object Model
- 原出版社: Addison-Wesley Professional
编辑推荐
如果你是一位C++程序员,渴望对于底层知识获得一个完整的了解,那么本书正适合你
内容简介
计算机书籍
作者Lippman参与设计了全世界第一套C++编译程序cfront,这本书就是一位伟大的C++编译程序设计者向你阐述他如何处理各种explicit(明确出现于C++程序代码中)和implicit(隐藏于程序代码背后)的C++语意。
《深度探索C++对象模型》专注于C++面向对象程序设计的底层机制,包括结构式语意、临时性对象的生成、封装、继承,以及虚拟——虚拟函数和虚拟继承。这本书让你知道:一旦你能够了解底层实现模型,你的程序代码将获得多么大的效率。Lippman澄清了那些关于C++额外负荷与复杂度的各种错误信息和迷思,但也指出其中某些成本和利益交换确实存在。他阐述了各式各样的实现模型,指出它们的进化之道及其本质因素。书中涵盖了C++对象模型的语意暗示,并指出这个模型是如何影响你的程序的。
对于C++底层机制感兴趣的读者,这必然是一本让你大呼过瘾的绝妙好书。
作译者
侯捷 是计算机技术书籍的作家、译者、书评人.著有《深入浅出MFC》、《多型与虚拟》,《STL源码剖析》、《无责任书评》三卷;泽有众多脍炙人U的高阶技术书籍,包括Meyers所著的“Effective C++”系列、,侯捷兼任教职于元智大学、同济大学、南京大学。他的个人网址是http://www.jjhou.com(中文繁体)和http://jjhou.csdn.net(中文简体)。
目录
本立道生(侯捷 译序) III
目录 VII
前言(Stanley B. Lippman) XIII
第0章 导读(译者的话) XXV
第1章 关于对象(Object Lessons) 1
加上封装后的布局成本(Layout Costs for Adding Encapsulation) 5
1.1 C++对象模式(The C++ Object Model) 6
简单对象模型(A Simple Object Model) 7
表格驱动对象模型(A Table-driven Object Model) 8
C++对象模型(The C++ Object Model) 9
对象模型如何影响程序(How the Object Model Effects Programs) 13
1.2 关键词所带来的差异(A Keyword Distinction) 15
关键词的困扰 16
策略性正确的struct(The Politically Correct Struct) 19
1.3 对象的差异(An Object Distinction) 22
指针的类型(The Type of a Pointer) 28
加上多态之后(Adding Polymorphism) 29
第2章 构造函数语意学(The Semantics of Constructors) 37
2.1 Default Constructor的构造操作 39
译者序
对于传统的结构化(sequential)语言,我们向来没有太多的疑惑,虽然在函数调用的背后,也有着堆栈建制、参数排列、返回地址、堆栈清除等等幕后机制,但函数调用是那么的自然而明显,好像只是夹带着一个包裹,从程序的某一个地点跳到另一个地点去执行。
但是对于面向对象(Object Oriented)语言,我们的疑惑就多了。究其因,这种语言的编译器为我们(程序员)做了太多的服务:构造函数、析构函数、虚拟函数、继承、多态……有时候它为我们合成出一些额外的函数(或运算符),有时候它又扩张我们所写的函数内容,放进更多的操作。有时候它还会为我们的objects加油添醋,放进一些奇妙的东西,使你面对sizeof的结果大惊失色。
我心里头一直有个疑惑:计算机程序最基础的形式,总是脱离不了一行一行的循序执行模式,为什么OO(面向对象)语言却能够“自动完成”这么多事情呢?另一个疑惑是,威力强大的polymorphism(多态),其底层机制究竟如何?
如果不了解编译器对我们所写的C++代码做了什么手脚,这些困惑永远解不开。
这本书解决了过去令我百思不解的诸多疑惑。我要向所有已具备C++多年程序设计经验的同好们大力推荐这本书。
这本书同时也是跃向组件软件(component-ware)基本精神的“跳板”。不管你想学习COM(Component Object Model)、CORBA(Common Object Request Broker Architecture)或是SOM(System Object Model),了解C++ Object Model,将使你更清楚软件组件(components)设计上的难点与运用之道。不但我自己在学习COM的道路上有此强烈的感受,Essential COM(《COM本质论》,侯捷译,碁峰1998)的作者Don Box也在他的书中推崇Lippman的这一本卓越的书籍。
是的,这当然不会是一本轻松的书籍。某些章节(例如3、4两章)可能给你立即的享受——享受于面对底层机制有所体会与掌控的快乐;某些章节(例如5、6、7三章)可能带给你短暂的痛苦——痛苦于艰难深涩、难以吞咽的内容。这些快乐与痛苦,其实就是我翻译此书时的心情写照。无论如何,我希望通过我的译笔,把这本难得的好书带到更多人面前,引领大家见识C++底层建设的技术之美。
侯捷 2011.10.20 于新竹
jjhou@ccca.nctu.edu.tw
请注意:本书特点,作者Lippman在其前言中有很详细的描述,我不再多言。翻译用词与心得,记录在第0章(译者的话)之中,对您或有导读之功。
请注意:原文本有大大小小约80~90个笔误。有的无伤大雅,有的影响阅读顺畅甚巨(如前后文所用符号不一致、内文与图形所用符号不一致——甚至因而导致图片的文字解释不正确)。我已在第0章(译者的话)列出所有我找到的错误。此外,某些场合我还会在错误出现之处再加注,表示原文内容为何。这么做不是画蛇添足,也不为彰显什么。我知道有些读者拿着原文书和中译书对照着看,我把原书错误加注出来,可免读者怀疑是否我打错字或是译错了。另一方面也是为了文责自负……唔……万一Lippman是对的而J.J.Hou错了呢?!我虽有相当把握,但还是希望明白摊开来让读者检验。
前言
Foundation项目是什么?在 Bjarne 的领导下,贝尔实验室中的一个小组探索着以C++完成大规模程序设计时的种种问题的解决之道。Foundation项目是我们为了构建大系统而努力定义的一个新的开发模型(我们只使用C++,并不提供多重语言的解决方案)。这是个令人兴奋的工作,一方面是因为工作本身,一方面是因为工作伙伴:Bjarne、Andy Koenig、Rob Murray、Martin Carroll、Judy Ward、Steve Buroff、Peter Juhl,以及我自己。Barbara Moo管理我们这一群人(Bjarne和Andy除外)。Barbara Moo常说管理一个软件团队,就像放牧一群骄傲的猫。
我们把Foundation想象成一个核心,在那上面,其他人可以为使用者铺设一层真正的开发环境,把它整修为他们所期望的UNIX或Smalltalk模型。私底下我们把它称为Grail(传说中耶稣最后晚餐所用的圣杯),人人都想要,但是从来没人找到过!
Grail使用一个由Rob Murray发展出来并命名为ALF的面向对象层次结构,提供一个永久的、以语意为基础的表现法。在Grail中,传统编译器被分解为数个各自分离的可执行文件。parser负责建立程序的ALF表现法。其他每一个组件(如type checking、simplification、code generation)以及工具(如browser)都在程序的一个ALF表现体上操作(并可能加以扩展)。Simplifier是编译器的一部分,处于type checking和code generation之间。Simplifier 这个名称是由Bjarne所倡议的,它原本是cfront的一个阶段(phase)。
在type checking和code generation之间,Simplifier做什么事呢?它用来转换内部的程序表现。有三种转换风味是任何对象模型都需要的:
1.与编译器息息相关的转换(Implementation-dependent transformations)
这是与特定编译器有关的转换。在ALF之下,这意味着我们所谓的“tentative”nodes。例如,当parser看到这个表达式:
fct();
它并不知道是否(a)这是一个函数调用操作,或者(b)这是overloaded call operator在class object fct上的一种应用。默认情况下,这个式子所代表的是一个函数调用,但是当(b)的情况出现,Simplifier 就要重写并调换 call subtree。
2.语言语意转换(Language semantics transformations)
这包括constructor/destructor的合成和扩展、memberwise初始化、对于memberwise copy的支持、在程序代码中安插conversion operators、临时性对象,以及对constructor/destructor的调用。
3.程序代码和对象模型的转换(Code and object model transformations)
这包括对virtual functions、virtual base class和inheritance的一般支持、new和delete运算符、class objects所组成的数组、local static class instances、带有非常量表达式(nonconstant expression)之global object的静态初始化操作。我对Simplifier所规划的一个目标是:提供一个对象模型体系,在其中,对象的实现是一个虚拟接口,支持各种对象模型。
最后两种类型的转换构成了本书的基础。这意味着本书是为编译器设计者而写的吗?不是,绝对不是!这本书是由一位编译器设计者针对中高级C++程序员所写的。隐藏在这本书背后的假设是,程序员如果了解C++对象模型,就可以写出比较没有错误倾向而且比较有效率的代码。
什么是C++对象模型
有两个概念可以解释C++对象模型:
1. 语言中直接支持面向对象程序设计的部分。
2. 对于各种支持的底层实现机制。
语言层面的支持,涵盖于我的C++ Primer一书以及其他许多C++书籍当中。至于第二个概念,则几乎不能够于目前任何读物中发现,只有[ELLIS90]和[STROUP94]勉强有一些蛛丝马迹。本书主要专注于C++对象模型的第二个概念。本书语言遵循C++委员会于1995冬季会议中通过的Standard C++草案(除了某些细节,这份草案应该能够反映出此语言的最终版本)。
C++对象模型的第一个概念是一种“不变量”。例如,C++ class的完整virtual functions在编译时期就固定下来了,程序员没有办法在执行期动态增加或取代其中的某一个。这使得虚拟调用操作得以快速地派送(dispatch)结果,付出的成本则是执行期的弹性。
媒体评论
下面是编译器设计者必须回答的三个主要问题:
1.编译器如何找出函数的定义?
答案之一是包含template program text file,就好像它是一个header文件一样。
Borland编译器就遵循这个策略。另一种方法是要求一个文件命名规则,例如,我们可以要求,在Point.h文件中发现的函数声明,其template program text一定要放置于文件Point.C或Point.cpp中,依此类推。cfront就遵循这个策略。Edison DesignGroup编译器对这两种策略都支持。
2.编译器如何能够只实例化程序中用到的member functions?
解决办法之一就是,根本忽略这项要求,把一个已经实例化的class的所有member functions都产生出来。Borland就是这么做的——虽然它也提供#pragmas让你压制(或实例化)特定实例。另一种策略就是模拟链接操作,检测看看哪一个函数真正需要,然后只为它(们)产生实例。cfront就是这么做的。Edison DesignGroup编译器对这两种策略都支持。
3.编译器如何阻止member definitions在多个.o文件中都被实例化呢?
解决办法之一就是产生多个实例,然后从链接器中提供支持,只留下其中一个实例,其余都忽略。另一个办法就是由使用者来导引“模拟链接阶段”的实例化策略,决定哪些实例(instances)才是所需求的。
目前,不论是编译时期还是链接时期的实例化(instantiation)策略,均存在以下弱点:当template实例被产生出来时,有时候会大量增加编译时间。很显然,这将是template functions第一次实例化时的必要条件。然而当那些函数被非必要地再次实例化,或是当“决定那些函数是否需要再实例化”所花的代价太大时,编译器的表现令人失望!
C++支持template的原始意图可以想见是一个由使用者导引的自动实例化机制(use—directed automatic instantiation mechanism),既不需要使用者的介入,也不需要相同文件有多次的实例化行为。但是这已被证明是非常难以达成的任务,比任何人此刻所能想象的还要难(请参考[S7ROUP94])。ptlink,随着cfront 3.0版所附的原始实例化工具,提供了一个由使用者驱动的自动实例化机制(use—drivenautomatic instantiation mechanism),但它实在太复杂了,即使是久经世故的人也没法一下子了解。
……
书摘
Instantiation)对于template的支持,最困难的莫过于template
function的实例化(instantiation)。目前的编译器提供了两个策略:一个是编译时期策略,程序代码必须在program
text file中备妥可用;
另一个是链接时期策略,有一些meta.compilation工具可以导引编译器的实例化行为(instantiation)。
下面是编译器设计者必须回答的三个主要问题:
1.编译器如何找出函数的定义?
答案之一是包含template program text
file,就好像它是一个header文件一样。
Borland编译器就遵循这个策略。另一种方法是要求一个文件命名规则,例如,我们可以要求,在Point.h文件中发现的函数声明,其template
program text一定要放置于文件Point.C或Point.cpp中,依此类推。cfront就遵循这个策略。Edison
DesignGroup编译器对这两种策略都支持。
2.编译器如何能够只实例化程序中用到的member functions?
解决办法之一就是,根本忽略这项要求,把一个已经实例化的class的所有member
functions都产生出来。Borland就是这么做的——虽然它也提供#pragmas让你压制(或实例化)特定实例。另一种策略就是模拟链接操作,检测看看哪一个函数真正需要,然后只为它(们)产生实例。cfront就是这么做的。Edison
DesignGroup编译器对这两种策略都支持。
3.编译器如何阻止member
definitions在多个.o文件中都被实例化呢?
解决办法之一就是产生多个实例,然后从链接器中提供支持,只留下其中一个实例,其余都忽略。另一个办法就是由使用者来导引“模拟链接阶段”的实例化策略,决定哪些实例(instances)才是所需求的。
目前,不论是编译时期还是链接时期的实例化(instantiation)策略,均存在以下弱点:当template实例被产生出来时,有时候会大量增加编译时间。很显然,这将是template