这是作者为《C++ Primer(第4版)(评注版)》写的序言,文中“本书”指的是这本书评注版。
B.1 为什么要学习C++
2009年本书作者Stanley Lippman先生应邀来华参加上海祝成科技举办的C++技术大会,他表示人们现在还用C++的唯一理由是其性能。相比之下,Java、C#、Python等语言更加易学易用并且开发工具丰富,它们的开发效率都高于C++。但C++目前仍然是运行最快的语言(见编程语言性能对比网站和Google员工写的语言性能对比论文(http://days2011.scala-lang.org/sites/days2011/files/ws3-1-Hundt.pdf)),如果你的应用领域确实在乎这个性能,那么C++是不二之选。
这里略举几个例子(C++之父Bjarne Stroustrup维护的C++用户列表)。对于手持设备而言,提高运行效率意味着完成相同的任务需要更少的电能,从而延长设备的操作时间,增强用户体验。对于嵌入式(初窥C++在嵌入式系统中的应用,参见http://aristeia.com/TalkNotes/MISRA_Day_2010.pdf)设备而言,提高运行效率意味着:实现相同的功能可以选用较低档的处理器和较少的存储器,降低单个设备的成本;如果设备销量达到一定规模,可以弥补C++开发的成本。对于分布式系统而言,提高10%性能就意味着节约10%的机器和能源。如果系统大到一定的规模(数千台服务器),值得用程序员的时间去换取机器的时间和数量,可以降低总体成本。另外,对于某些延迟敏感的应用(游戏(Milo Yip在《C++强大背后》提到大部分游戏引擎(如Unreal/Source(起源引擎))及中间件(如Havok(用于游戏和模拟中的物理模拟)/FMOD(用于处理音频资源、音效设计和音频播放等功能))是C++实现的(http://www.cnblogs.com/miloyip/archive/2010/09/17/behind_cplusplus.html)),金融交易),通常不能容忍垃圾收集(GC)带来的不确定延时,而C++可以自动并精确地控制对象销毁和内存释放时机(参见孟岩的《垃圾收集机制批判》:“C++利用智能指针达成的效果是,一旦某对象不再被引用,系统刻不容缓,立刻回收内存。这通常发生在关键任务完成后的清理(clean up)时期,不会影响关键任务的实时性,同时,内存里所有对象都是有用的,绝对没有垃圾空占内存。”,http://blog.csdb.net/myan/article/details/1906)。作者曾经不止一次见到,出于性能(特别是及时性方面的)原因,用C++重写现有的Java或C#程序。
C++之父Bjarne Stroustrup把C++定位于偏重系统编程(system programming)(有人半开玩笑地说:“所谓系统编程,就是那些CPU时间比程序员的时间更重要的工作。”)的通用程序设计语言,开发信息基础架构(infrastructure)是C++的重要用途之一(《Software Development for Infrastructure》)。Herb Sutter总结道(Herb Sutter在C++ and Beyond 2011会议上的开场演讲:《Why C++?》),C++注重运行效率(efficiency)、灵活性(flexibility)(这里的灵活性指的是编译器不阻止你干你想干的事情,比如为了追求运行效率而实现即时编译(just-in-time compilation,JIT编译,一种在程序运行时将代码转换为机器代码的技术。与传统的静态编译不同,JIT编译是在程序运行时动态地进行代码转换))和抽象能力(abstraction),并为此付出了生产力(productivity)方面的代价(作者曾向Stanley Lippman介绍目前作者在Linux下的工作环境(编辑器、编译器、调试器),他表示这跟他在1970年代的工作环境相差无几,可见C++在开发工具方面的落后。另外C++的编译运行调试周期也比现代的语言长,这多少影响了工作效率)。用本书作者的话来说,就是“C++ is about efficient programming with abstractions”(C++的核心价值在于能写出“运行效率不打折扣的抽象”)。
要想发挥C++的性能优势,程序员需要对语言本身及各种操作的代价有深入的了解(《Technical Report on C++Performance》,http://www.open-std.org/jtc1/sc22/wg21/docs/18015.html),特别要避免不必要的对象创建(可参考Scott Meyers的《Effective C++ in an Embedded Environment》讲义,http://www.artima.com/shop/effective_cpp_in_an_embedded_environment)。例如下面这个函数如果漏写了&,功能还是正确的,但性能将会大打折扣。编译器和单元测试都无法帮我们查出此类错误,程序员自己在编码时须得小心在意。
inline int find_longest(const std::vector<std::string> &words)
{// std::max_element(words.begin(), words.end(), LengthCompare());
}
在现代 CPU体系结构下,C++的性能优势很大程度上得益于对内存布局(memory layout)的精确控制,从而优化内存访问的局部性(locality of reference)并充分利用内存阶层(memory hierarchy,内存阶层通常由寄存器、高速缓存、主存和辅助存储器组成)提速(我们知道std::list的任一位置插入是O(1)操作,而std::vector的任一位置插入是O(N)操作,但由于vector的元素布局更加紧凑(compact),很多时候vector的随机插入性能甚至会高于list。这也佐证vector是首选容器)。可参考Scott Meyers的讲义《CPU Caches and Why You Care》(http://aristeia.com/TalkNotes/ACCU2011_CPUCaches.pdf)、Herb Sutter的讲义《Machine Architecture》和任何一本现代的计算机体系结构教材(《计算机体系结构:量化研究方法》、《计算机组成与设计:硬件/软件接口》、《深入理解计算机系统》等)。这一点优势在近期内不会被基于GC的语言赶上(Bjarne Stroustrup有一篇论文《Abstraction and the C++ machine model》对比了C++和Java的对象内存布局)。
C++的协作性不如C、Java、Python,开源项目也比这几个语言少得多,因此在TIOBE语言流行榜(一种用于衡量编程语言流行程度的指标)中节节下滑。但是据作者所知,很多企业内部使用C++来构建自己的分布式系统基础架构,并且有替换Java开源实现的趋势。
B.2 学习C++只需要读一本大部头
C++不是特性(features)最丰富的语言,却是最复杂的语言,诸多语言特性相互干扰,使其复杂度成倍增加。鉴于其学习难度和知识点之间的关联性,恐怕不能用“粗粗看看语法,就撸起袖子开干,边查Google边学习”(语出孟岩《快速掌握一个语言最常用的50%》,http://blog.csdn.net/myan/article/details/3144661)这种方式来学习C++,那样很容易掉到陷阱里或养成坏的编程习惯。如果想成为专业C++开发者,全面而深入地了解这门复杂语言及其标准库,你需要一本系统而权威(“权威”的意思是说你不用担心作者讲错了,能达到这个水准的C++图书作者全世界也屈指可数)的书,这样的书必定会是一本八九百页的大部头(同样篇幅的Java、C#、Python教材可以从语言、标准库一路讲到多线程、网络编程、图形编程)。
兼具系统性和权威性的C++教材有两本,C++之父Bjarne Stroustrup的代表作《The C++ Programming Language》和Stanley Lippman的这本《C++ Primer》。侯捷先生评价道:“泰山北斗已现,又何必案牍劳形于墨瀚书海之中!这两本书都从C++盘古开天以来,一路改版,斩将擎旗,追奔逐北,成就一生荣光。”(侯捷《大道之行也——C++ Primer 3/e译序》)
从实用的角度,这两本书读一本即可,因为它们覆盖的C++知识点相差无几。就作者个人的阅读体验而言,Primer更易读一些,作者10年前深入学习C++正是用的《C++ Primer(第3版)》。这次借评注的机会仔细阅读了《C++ Primer(第4版)》,感觉像在读一本完全不同的新书。第4版内容组织及文字表达比第3版进步很多(Bjarne Stroustrup在《Programming——Principles and Practice Using C++》的参考文献中引用了本书,并特别注明“use only the 4th edition”),第3版可谓“事无巨细、面面俱到”,第4版则重点突出、详略得当,甚至篇幅也缩短了,这多半归功于新加盟的作者Barbara Moo。
《C++ Primer(第4版)》讲什么?适合谁读?
这是一本C++语言的教程,不是编程教程。本书不讲八皇后问题、Huffman编码、汉诺塔、约瑟夫环、大整数运算等经典编程例题,本书的例子和习题往往都跟C++本身直接相关。本书的主要内容是精解C++语法(syntax)与语意(semantics),并介绍C++标准库的大部分内容(含STL)。“这本书在全世界C++教学领域的突出和重要,已经无须我再赘言。”(侯捷《C++ Primer 4/e译序》)
本书适合C++语言的初学者,但不适合编程初学者。换言之,这本书可以是你的第一本C++书,但恐怕不能作为第一本编程书。如果你不知道什么是变量、赋值、分支、条件、循环、函数,你需要一本更加初级的书(如果没有时间精读《Programming——Principles and Practice Using C++》这本大部头,短小精干的《Accelerated C++》亦是上佳之选。另外如果想从C语言入手,作者推荐裘宗燕老师的《从问题到程序:程序设计与C语言引论》(用最新版)),本书第1章可用做自测题。
如果你已经学过一门编程语言,并且打算成为专业C++开发者,从《C++ Primer(第4版)》入手不会让你走弯路。值得特别说明的是,学习本书不需要事先具备C语言知识。相反,这本书叫你编写真正的C++程序,而不是披着C++外衣的C程序。
《C++ Primer(第4版)》的定位是语言教材,不是语言规格书,它并没有面面俱到地谈到C++的每一个角落,而是重点讲解C++程序员日常工作中真正有用的、必须掌握的语言设施和标准库(本书把iostream的格式化输出放到附录,彻底不谈locale/facet,可谓匠心独运)。本书的作者一点也不炫耀自己的知识和技巧,虽然他们有十足的资本(Stanley Lippman曾说:“Virtual base class support wanders off into the Byzantine… The material is simply too esoteric to warrant discussion…”(虚拟基类的支持变得非常复杂难懂,好像进入了拜占庭迷宫一样。而且这个话题非常深奥,不值得讨论))。这本书用词非常严谨(没有那些似是而非的比喻),用词平和,讲解细致,读起来并不枯燥。特别是如果你已经有一定的编程经验,在阅读时不妨思考如何用C++来更好地完成以往的编程任务。
尽管本书篇幅近900页,但其内容还是十分紧凑的,很多地方读一个句子就值得写一小段代码去验证。为节省篇幅,本书经常修改前文代码中的一两行,来说明新的知识点,值得把每一行代码敲到机器中去验证。习题当然也不能轻易放过。
《C++ Primer(第4版)》体现了现代C++教学与编程理念:在现成的高质量类库上构建自己的程序,而不是什么都从头自己写。这本书在第3章介绍了string和vector这两个常用的class,立刻就能写出很多有用的程序。但作者不是一次性把string的上百个成员函数一一列举,而是有选择地先讲解了最常用的那几个函数,充分体现了本书作为教材而不是手册的定位。
《C++ Primer(第4版)》的代码示例质量很高,不是那种随手写的玩具代码。第10.4.2节实现了带禁用词的单词计数,第10.6利用标准库容器简洁地实现了基于倒排索引思路的文本检索,第16.9节又用面向对象方法扩充了文本检索的功能,支持布尔查询。值得一提的是,这本书讲解继承和多态时举的例子符合Liskov替换原则(根据这个原则,任何基类的对象都应该能够被其子类对象替换,而不影响程序的正确性),是正宗的面向对象。相反,某些教材以复用基类代码为目的,常以“人、学生、老师、教授”或“雇员、经理、销售、合同工”为例,这是误用了面向对象的“复用”。
《C++ Primer(第4版)》出版于2005年,遵循2003年的C++语言标准(基本等同于1998年的出版C++标准,修正了编译器作者关心的一些问题,与普通程序员无关)。C++新标准已于2011年定案(称为C++11),本书不涉及TR1(TR1是2005年的C++标准库的一次扩充,增加了智能指针、bind/function、哈希表、正则表达式等)和C++11,这并不意味着这本书过时了(第4版的作者正在编写《C++ Primer(第5版)》,会包含C++11的内容)。相反,这本书里沉淀的都是当前广泛使用的C++编程实践,学习它可谓正当时。评注版也不会越俎代庖地介绍这些新内容,但是会指出哪些语言设施已在新标准中废弃,避免读者浪费精力。
《C++ Primer(第4版)》是平台中立的,并不针对特定的编译器或操作系统。目前最主流的C++编译器有两个,GNU G++和微软Visual C++。实际上,这两个编译器阵营基本上“模塑”(G++统治了Linux,并且能用在很多Unix系统上;Visual C++统治了Windows。其他C++编译器的行为通常要向它们靠拢,例如Intel C++在Linux上要兼容G++,而在Windows上要兼容Visual C++)了C++语言的行为。理论上讲,C++语言的行为是由C++标准规定的。但是C++不像其他很多语言有“官方参考实现”(曾经是Cfront,本书作者正是其主要开发者,http://www.softwarepreservation.org/projects/c_plus_plus),因此C++的行为实际上是由语言标准、几大主流编译器、现有不计其数的C++产品代码共同确定的,三者相互制约。C++编译器不光要尽可能符合标准,同时也要遵循目标平台的成文或不成文规范和约定,例如高效地利用硬件资源、兼容操作系统提供的C语言接口等等。在C++标准没有明文规定的地方,C++编译器也不能随心所欲地自由发挥。学习C++的要点之一是明白哪些行为是由标准保证,哪些是由实现(软硬件平台和编译器)保证的(包括C++标准有规定,但编译器拒绝遵循的,http://stackoverflow.com/questions/3931312),哪些是编译器自由实现,没有保证的;换言之,明白哪些程序行为是可依赖的。从学习的角度,作者建议如果有条件不妨两个编译器都用,相互比照,避免把编译器和平台特定的行为误解为C++语言规定的行为(G++是免费的,可使用较新的4.x版,最好32-bit和64-bit一起用,因为服务端已经普及64-bit编程。微软也有免费的C++编译器,可考虑用Visual C++ 2010 Express(Express指它是针对学术和个人使用的免费版本),建议不要用老掉牙的Visual C++ 6.0作为学习平台)。尽管不是每个人都需要写跨平台的代码,但也大可不必自我限定在编译器的某个特定版本,毕竟编译器是会升级的。
本着“练从难处练,用从易处用”的精神,建议在命令行下编译运行本书的示例代码,并尽量少用调试器。另外,值得了解C++的编译链接模型(可参考陈硕写的《C++工程实践经验谈》中的“C++编译模型精要”一节(本书第十章)),这样才能不被实际开发中遇到的编译错误或链接错误绊住手脚(C++不像现代语言那样有完善的模块(module)和包(packet)设施,它从C语言继承了头文件、源文件、库文件等古老的模块化机制,这套机制相对较为脆弱,需要花一定时间学习规范的做法,避免误用)。
就学习C++语言本身,有几个练习非常值得一做。这不是“重复发明轮子”,而是必要的编程练习,帮助你熟悉、掌握这门语言。一是写一个复数类或者大整数类(大整数类可以以std::vector为成员变量,避免手动资源管理),实现基本的加减乘运算,熟悉封装与数据抽象。二是写一个字符串类,熟悉内存管理与拷贝控制。三是写一个简化的vector<T>
类模板,熟悉基本的模板编程,你的这个vector应该能放入int和std::string等元素类型。四是写一个表达式计算器,实现一个节点类的继承体系(图B-1 右),体会面向对象编程。前三个练习是写独立的值语义的类,第四个练习是对象语义,同时要考虑类与类之间的关系。
表示式计算器能把四则运算式3+2×4解析为图B-1左图的表达式树(“解析”可以用数据结构课程介绍的逆波兰表达式方法,也可以用编译原理中介绍的递归下降法,还可以用专门的Packrat算法),对根节点调用calculate()虚函数就能算出表达式的值。做完之后还可以再扩充功能,比如支持三角函数和变量。
在写完面向对象版的表达式树之后,还可以略微尝试泛型编程。比如把类的继承体系简化为图B-2,然后用BinaryNode<std::plus<double>>
和BinaryNode<std::multipies<double>>
来具现化BinaryNode<T>
类模板(std::plus和std::multipies类对象可接受两个参数来实现加和乘),通过控制模板参数的类型来实现不同的运算。
在表达式树这个例子中,节点对象时动态创建的,值得思考:如何才能安全地、不重不漏地释放内存。本书第15.8节的Handle可供参考(C++的面向对象基础设施相对于现代的语言而言显得很简陋,现在C++也不再以“支持面向对象”为卖点了)。
C++难学吗?“能够靠读书、看文章、读代码、做练习学会的东西没什么门槛,智力正常的人只要愿意花工夫,到不难达到(不错)的程度。”(孟岩《技术路线的选择重要但不具有决定性》,http://blog.csdn.net/myan/article/details/3247071)C++好书很多,不过优秀的C++开源代码很少,而且风格迥异(从代码风格上往往能判断项目成型的时代)。这里按个人口味和经验列几个供读者参考阅读:Google的Protobuf、leveldb(一个开源的、快速、轻量级的键值存储引擎,由Google开发)、PCRE(Perl Compatible Regular Expressions,一个支持Perl正则表达式语法的正则表达式库)的C++封装,作者自己写的muduo网络库。这些代码都不长,功能明确,阅读难度不大。如果有时间,还可以读一读Chromium中的基础库源码。在读Google开源的C++代码时要连注释一起细读。不建议一开始就读STL或Boost的源码,因为编写通用C++模板库和编写C++应用程序的知识体系相差很大。另外可以考虑读一些优秀的C或Java开源项目,并思考是否可以用C++更好地实现或封装之(特别是资源管理方面能否避免手动清理)。
B.3 继续前进
作者能够随手列出十几本C++好书,但是从实用角度出发,这里只举两三本必读的书。读过《C++ Primer》和这几本好书之后,想必读者已能自行识别C++图书的优劣,可以根据项目需要加以钻研。
第一本是《Effective C++中文版(第3版)》(Scott Meyers著,侯捷译,电子工业出版社出版)[EC3]。学习语法是一回事,高效地运用这门语言是另一回事。C++是一个遍布陷阱的语言,吸取专家经验尤为重要,既能快速提高眼界,又能避免重蹈覆辙。加上《C++ Primer》这本书包含的C++知识足以应付日常应用程序开发。
作者假定读者一定会阅读这本书,因此在评注中不引用(《Effective C++中文版(第3版)》)的任何章节。
《Effective C++中文版(第3版)》的内容也反映了C++用法的进步。第2版建议“总是让基类拥有虚析构函数”,第3版改为“为多态基类声明虚析构函数”。因为在C++中,“继承”不光只有面向对象这一种用途,即C++的继承不一定是为了覆写(override)基类的虚函数。第2版花了很多笔墨介绍浅拷贝和深拷贝,以及对指针成员变量的处理(Andrew Koenig的《Teaching C++ Badly: Introduce Constructors and Destructors at the Same Time》,http://drdobbs.com/blogs/cpp/229500116)。第3版则提议,对于多数class而言,要么直接禁用拷贝构造函数和赋值操作符,要么通过选用合适的成员变量类型(能自动管理资源的std::string、std::vector、boost::shared_ptr等等,这样多数class连析构函数都不必写),使得编译器默认生成的这两个成员函数就能正常工作。
什么是C++编程中最重要的编程技法(idiom)?作者认为是“用对象来管理资源”,即RAII。资源包括动态分配的内存(“分配内存”包括在堆(heap)上创建对象),也包括打开的文件、TCP网络连接、数据库连接、互斥锁等等。借助RAII,我们可以把资源管理和对象生命期管理等同起来,而对象生命期管理在现代C++里根本不困难(用智能指针),只需要花几天时间熟悉几个智能指针(包括TR1中的shared_ptr、weak_ptr,还有更简单的boost::scoped_ptr)的基本用法即可。学会了这三招两式,现代的C++程序中可以完全不写delete,也不必为指针和内存错误操心。现在C++程序里出现资源和内存泄漏的唯一可能是循环引用,一旦发现,也很容易修正设计和代码。这方面的详细内容请参考《Effective C++中文版(第3版)》的第3章“资源管理”。
C++是目前唯一能实现自动化资源管理的语言,C语言完全靠手工释放资源,而其他基于垃圾收集的语言只能能自动清理内存,而不能自动清理其他资源(Java 7有try-with-resources语句,Python有with语句,C#有using语句,可以自动清理栈上的资源,但对生命期大于局部作用域的资源无能为力,需要程序员手工管理)(网络连接,数据库连接等)。
除了智能指针,TR1中的bind/function也十分值得投入精力去学一学(孟岩的《function/bind的救赎(上)》,http://blog.csdn.net/myan/article/details/5928531)。让你从一个崭新的视角,重新审视类与类之间的关系。Stephan T. Lavavej有一套PPT介绍TR1的这几个主要部件(http://blogs.msdn.com/b/vcblog/archive/2008/02/22/tr1-slide-decks.aspx)。
第二本书,如果读者还是在校学生,已经学过数据结构课程(最好再学一点基础的离散数学)的话,可以考虑读一读《泛型编程与STL》(Matthew Austern著,侯捷译,中国电力出版社);如果已经工作,学完《C++ Primer》立刻就要参加C++项目开发,那么推荐阅读《C++编程规范》(Herb Sutter等著,刘基诚译,人民邮电出版社出版,这本书的繁体版由侯捷先生和作者翻译)[CSS]。
泛型编程有一套自己的术语,如concept(指对模板参数的一组要求或限制,它描述了模板参数所需要具备的特定特性,例如某个类型必须具有某种成员函数、某种操作符重载等等)、model(指满足某个concept的具体类型或模板)、refinement(指在一个已定义的concept的基础上,对其进行扩展和补充,以适应更特定的需求和要求,通过refinement,我们可以在已有的concept的基础上添加新的要求、约束和行为,从而创建一个更具体、更精确的concept)等等,理解这套术语才能阅读泛型程序库的文档。即便不掌握泛型编程作为一种程序设计方法,也要掌握C++中以泛型思维设计出来的标准容器库和算法库(STL)。坊间面向对象的书琳琅满目,学习机会也很多,而泛型编程只有这么一本,读之可以开阔视野,并且加深对STL的理解(特别是迭代器(侯捷先生的《芝麻开门:从Iterator谈起》))和应用。
C++模板是一种强大的抽象手段,作者不赞同每个人都把精力花在钻研艰深的模板语法和技巧上。从实用角度,能在应用程序中写写简单的函数模板和类模板即可(以type traits(类型特性,如是否是常量类型(const)、是否是指针类型、是否是引用类型等,C++标准库提供了一组类型特性模板类和函数,如std::is_const、std::is_pointer、std::is_reference)为限),并非每个人都要去写公用的模板库。
由于C++语言过于庞大复杂,作者见过的开发团队都对其剪裁使用(孟岩的《编程语言的层次观点——兼谈C++的剪裁方案》,http://blog.csdn.net/myan/article/details/1920)。往往团队越大,项目成立时间越早,剪裁得越厉害,也越接近C。制定一份好的编程规范相当不容易。若规范定得太紧(比如定位团队成员知识能力的交集),程序员束手束脚,限制了生产力,对程序员个人发展也不利(一个人通常不会在一个团队工作一辈子,其他团队可能有不同的C++剪裁使用方式,程序员要有“一桶水”的本事,才能应付不同形状大小的水碗)。若规范定得太松(定位团队成员知识能力的并集),项目内代码风格迥异,学习交流协作成本上升,恐怕对生产力也不利。由两位顶级专家合写的《C++编程规范》一书可谓是现代C++编程规范的范本。
《C++编程规范》同时也是专家经验一类的书,这本书篇幅比《Effective C++中文版(第3版))》短小,条款数目却多了近一倍,可谓言简意赅。有的条款看了就明白,照做即可:
1.第1条,以告警高级别编译代码,确保编译器无警告。
2.第31条,避免写出依赖于函数实参求值顺序的代码。C++操作符的优先级、结合性与表达式的求值顺序是无关的。裘宗燕老师写的《C/C++语言中表达式的求值》(http://www.math.pku.edu.cn/teachers/qiuzy/technotes/expression2009.pdf)一文对此有明确的说明。
3.第35条,避免继承“并非设计作为基类使用”的class。
4.第43条,明智地使用pimpl。这是编写C++动态链接库的必备手法,可以最大限度地提高二进制兼容性。
5.第56条,尽量提供不会失败的swap()函数。有了swap()函数,我们在自定义赋值操作符时就不必检查自赋值了。
6.第59条,不要在头文件中或#include之前写using。
7.第73条,以by value方式抛出异常(这样在异常被捕获后,即使异常抛出的作用域已经结束,捕获的代码仍然可以访问异常对象的副本),以by reference方式捕获异常(通过引用传递不需要额外的拷贝)。
8.第76条,优先考虑vector,其次再选择适当的容器。
9.第79条,容器内只可存放value和smart pointer。
有的条款则需要相当的设计与编码经验才能解其中三味:
1.第5条,为每个物体(entity)分配一个内聚任务。
2.第6条,正确性、简单性、清晰性居首。
3.第8、9条,不要过早优化;不要过早劣化。
4.第22条,将依赖关系最小化。避免循环依赖。
5.第32条,搞清楚你写的是哪一种class。明白value class、base class、trait class(常见用途是实现类型特征,例如判断一个类型是否具有某种成员函数、是否满足某种概念或属性等,通过使用Trait class,可以根据类型的特征来编写通用的代码,而不需要依赖具体的类型)、policy class(可作为模板实参传递给其他模板类,如一个内存管理模板类,对于内存分配方式,我们可以写多个policy class,如动态分配policy class、静态分配policy class等,然后根据需要将某个policy class传递给内存管理模板类)、exception class各有其作用,写法也不尽相同。
7.第33条,尽可能写小型class,避免写出“大怪兽(monolithic class)”。
8.第37条,public继承意味着可替换性。继承非为复用,乃为被复用。
9.第57条,将class类型及其非成员函数接口放入同一个namespace。
值得一提的是,《C++编程规范》是出发点,但不是一份终极规范。例如Google的C++编程规范和LLVM编程规范(http://llvm.org/docs/CodingStandards.html#ci_rtti_exceptions)都明确禁用异常,这跟这本书的推荐做法正好相反。
B.4 评注版使用说明
评注版采用大16开印刷,在保留原书版式的前提下,对其进行了重新分页,评注的文字与正文左右分栏并列排版。另外,本书已依据原书2010年第11次印刷的版本进行了全面修订。为了节省篇幅,原书每章末尾的小结、术语表及书末的索引都没有印在评注版中,而是做成PDF供读者下载,这也方便读者检索。评注的目的是帮助初次学习C++的读者快速深入掌握这门语言的核心知识,澄清一些概念、比较与其他语言的不同、补充实践中的注意事项等。评注的内容约占全书篇幅的15%,大致比例是三分评、七分注,并有一些补白的内容(第10章绘制了数据结构示意图,第11章补充lower_bound和upper_bound的示例)。如果读者拿不定主意是否购买,可以先翻一翻第5章。作者在评注中不谈C++11(从Scott Meyers的讲义可以快速学习C++11,http://www.artima.com/shop/overview_of_the_new_cpp),但会略微涉及TR1,因为TR1已经投入实用。
为了不打断读者阅读的思路,评注中不会给URL链接,评注中偶尔会引用《C++编程规范》的条款,以[CSS]标明,这些条款的标题已在前文列出。另外评注中出现的soXXXXXX表示http://stackoverflow.com/questions/XXXXXX网址。