从《C++ Primer 第四版》入手学习 C++

《C++ Primer 第4版 评注版》即将出版,这是序言。PDF 版见:

https://github.com/downloads/chenshuo/documents/LearnCpp.pdf

从《C++ Primer 第四版》入手学习 C++

为什么要学习C++?

2009 年本书作者 Stan Lippman 先生来华参加上海祝成科技举办的C++技术大会,他表示人们现在还用C++的惟一理由是其性能。相比之下,Java/C#/Python等语言更加易学易用并且开发工具丰富,它们的开发效率都高于C++。但C++目前仍然是运行最快的语言[1],如果你的应用领域确实在乎这个性能,那么 C++ 是不二之选。

这里略举几个例子[2]。对于手持设备而言,提高运行效率意味着完成相同的任务需要更少的电能,从而延长设备的操作时间,增强用户体验。对于嵌入式[3]设备而言,提高运行效率意味着:实现相同的功能可以选用较低档的处理器和较少的存储器,降低单个设备的成本;如果设备销量大到一定的规模,可以弥补C++开发的成本。对于分布式系统而言,提高10%的性能就意味着节约10%的机器和能源。如果系统大到一定的规模(数千台服务器),值得用程序员的时间去换取机器的时间和数量,可以降低总体成本。另外,对于某些延迟敏感的应用(游戏[4],金融交易),通常不能容忍垃圾收集(GC)带来的不确定延时,而C++可以自动并精确地控制对象销毁和内存释放时机[5]。我曾经不止一次见到,出于性能原因,用C++重写现有的Java或C#程序。

C++之父Bjarne Stroustrup把C++定位于偏重系统编程(system programming) [6]的通用程序设计语言,开发信息基础架构(infrastructure)是C++的重要用途之一[7]。Herb Sutter总结道[8],C++注重运行效率(efficiency)、灵活性(flexibility)[9]和抽象能力(abstraction),并为此付出了生产力(productivity)方面的代价[10]。用本书作者的话来说,C++ is about efficient programming with abstractions。C++的核心价值在于能写出“运行效率不打折扣的抽象[11]”。

要想发挥C++的性能优势,程序员需要对语言本身及各种操作的代价有深入的了解[12],特别要避免不必要的对象创建[13]。例如下面这个函数如果漏写了&,功能还是正确的,但性能将会大打折扣。编译器和单元测试都无法帮我们查出此类错误,程序员自己在编码时须得小心在意。

inline int find_longest(const std::vector<std::string>& words)
{
  // std::max_element(words.begin(), words.end(), LengthCompare());
}

在现代CPU体系结构下,C++ 的性能优势很大程度上得益于对内存布局(memory layout )的精确控制,从而优化内存访问的局部性[14](locality of reference)并充分利用内存阶层(memory hierarchy)提速[15],这一点优势在近期内不会被基于GC的语言赶上[16]

C++的协作性不如C、Java、Python,开源项目也比这几个语言少得多,因此在TIOBE语言流行榜中节节下滑。但是据我所知,很多企业内部使用C++来构建自己的分布式系统基础架构,并且有替换Java开源实现的趋势。

学习C++只需要读一本大部头

C++不是特性(features)最丰富的语言,却是最复杂的语言,诸多语言特性相互干扰,使其复杂度成倍增加。鉴于其学习难度和知识点之间的关联性,恐怕不能用“粗粗看看语法,就撸起袖子开干,边查Google边学习[17]”这种方式来学习C++,那样很容易掉到陷阱里或养成坏的编程习惯。如果想成为专业C++开发者,全面而深入地了解这门复杂语言及其标准库,你需要一本系统而权威的书,这样的书必定会是一本八九百页的大部头[18]

兼具系统性和权威性[19]的C++教材有两本,C++之父Bjarne Stroustrup的代表作《The C++ Programming Language》和Stan Lippman的这本《C++ Primer》。侯捷先生评价道:“泰山北斗已现,又何必案牍劳形于墨瀚书海之中!这两本书都从C++盘古开天以来,一路改版,斩将擎旗,追奔逐北,成就一生荣光[20]。”

从实用的角度,这两本书读一本即可,因为它们覆盖的C++知识点相差无几。就我个人的阅读体验而言,Primer更易读一些,我十年前深入学习C++正是用的《C++ Primer第三版》。这次借评注的机会仔细阅读了《C++ Primer第四版》,感觉像在读一本完全不同的新书。第四版内容组织及文字表达比第三版进步很多[21],第三版可谓“事无巨细、面面俱到”,第四版重点突出详略得当,甚至篇幅也缩短了,这多半归功于新加盟的作者Barbara Moo。

《C++ Primer 第四版》讲什么?适合谁读?

这是一本C++语言的教程,不是编程教程。本书不讲八皇后问题、Huffman编码、汉诺塔、约瑟夫环、大整数运算等等经典编程例题,本书的例子和习题往往都跟C++本身直接相关。本书的主要内容是精解C++语法(syntax)与语意(semantics),并介绍C++标准库的大部分内容(含STL)。“这本书在全世界C++教学领域的突出和重要,已经无须我再赘言[22]。”

本书适合C++语言的初学者,但不适合编程初学者。换言之,这本书可以是你的第一本C++ 书,但恐怕不能作为第一本编程书。如果你不知道什么是变量、赋值、分支、条件、循环、函数,你需要一本更加初级的书[23],本书第1章可用作自测题。

如果你已经学过一门编程语言,并且打算成为专业C++开发者,从《C++ Primer 第四版》入手不会让你走弯路。值得特别说明的是,学习本书不需要事先具备C语言知识。相反,这本书教你编写真正的C++程序,而不是披着C++ 外衣的C程序。

《C++ Primer 第四版》的定位是语言教材,不是语言规格书,它并没有面面俱到地谈到C++的每一个角落,而是重点讲解C++程序员日常工作中真正有用的、必须掌握的语言设施和标准库[24]。本书的作者一点也不炫耀自己的知识和技巧,虽然他们有十足的资本[25]。这本书用语非常严谨(没有那些似是而非的比喻),用词平和,讲解细致,读起来并不枯燥。特别是如果你已经有一定的编程经验,在阅读时不妨思考如何用C++来更好地完成以往的编程任务。

尽管本书篇幅近900页,其内容还是十分紧凑,很多地方读一个句子就值得写一小段代码去验证。为了节省篇幅,本书经常修改前文代码中的一两行,来说明新的知识点,值得把每一行代码敲到机器中去验证。习题当然也不能轻易放过。

《C++ Primer 第四版》体现了现代C++教学与编程理念:在现成的高质量类库上构建自己的程序,而不是什么都从头自己写。这本书在第三章介绍了string和vector这两个常用的类,立刻就能写出很多有用的程序。但作者不是一次性把string的上百个成员函数一一列举,而是有选择地讲解了最常用的那几个函数。

《C++ Primer 第四版》的代码示例质量很高,不是那种随手写的玩具代码。第10.4.2节实现了带禁用词的单词计数,第10.6利用标准库容器简洁地实现了基于倒排索引思路的文本检索,第15.9节又用面向对象方法扩充了文本检索的功能,支持布尔查询。值得一提的是,这本书讲解继承和多态时举的例子符合Liskov替换原则,是正宗的面向对象。相反,某些教材以复用基类代码为目的,常以“人、学生、老师、教授”或“雇员、经理、销售、合同工”为例,这是误用了面向对象的“复用”。

《C++ Primer 第四版》出版于2005年,遵循2003年的C++语言标准[26]。C++新标准已于2011年定案(称为C++11),本书不涉及TR1[27]和C++11,这并不意味着这本书过时了[28]。相反,这本书里沉淀的都是当前广泛使用的C++编程实践,学习它可谓正当时。评注版也不会越俎代庖地介绍这些新内容,但是会指出哪些语言设施已在新标准中废弃,避免读者浪费精力。

《C++ Primer 第四版》是平台中立的,并不针对特定的编译器或操作系统。目前最主流的C++编译器有两个, GNU G++和微软Visual C++。实际上,这两个编译器阵营基本上“模塑[29]”了C++语言的行为。理论上讲, C++语言的行为是由C++标准规定的。但是 C++不像其他很多语言有“官方参考实现[30]”,因此C++的行为实际上是由语言标准、几大主流编译器、现有不计其数的C++产品代码共同确定的,三者相互制约。C++编译器不光要尽可能符合标准,同时也要遵循目标平台的成文或不成文规范和约定,例如高效地利用硬件资源、兼容操作系统提供的C语言接口等等。在C++标准没有明文规定的地方,C++编译器也不能随心所欲自由发挥。学习C++的要点之一是明白哪些行为是由标准保证的,哪些是由实现(软硬件平台和编译器)保证的[31],哪些是编译器自由实现,没有保证的;换言之,明白哪些程序行为是可依赖的。从学习的角度,我建议如果有条件不妨两个编译器都用[32],相互比照,避免把编译器和平台特定的行为误解为C++语言规定的行为。尽管不是每个人都需要写跨平台的代码,但也大可不必自我限定在编译器的某个特定版本,毕竟编译器是会升级的。

本着“练从难处练,用从易处用”的精神,我建议在命令行下编译运行本书的示例代码,并尽量少用调试器。另外,值得了解C++的编译链接模型[33],这样才能不被实际开发中遇到的编译错误或链接错误绊住手脚。(C++不像现代语言那样有完善的模块(module)和包(package)设施,它从C语言继承了头文件、源文件、库文件等古老的模块化机制,这套机制相对较为脆弱,需要花一定时间学习规范的做法,避免误用。)

就学习C++语言本身而言,我认为有几个练习非常值得一做。这不是“重复发明轮子”,而是必要的编程练习,帮助你熟悉掌握这门语言。是写一个复数类或者大整数类[34],实现基本的运算,熟悉封装与数据抽象。是写一个字符串类,熟悉内存管理与拷贝控制。是写一个简化的vector<T>类模板,熟悉基本的模板编程,你的这个vector应该能放入int和string等元素类型。是写一个表达式计算器,实现一个节点类的继承体系(右图),体会面向对象编程。前三个练习是写独立的值语义的类,第四个练习是对象语义,同时要考虑类与类之间的关系。

表达式计算器能把四则运算式3+2*4解析为左图的表达式树[35],对根节点调用calculate()虚函数就能算出表达式的值。做完之后还可以再扩充功能,比如支持三角函数和变量。


在写完面向对象版的表达式树之后,还可以略微尝试泛型编程。比如把类的继承体系简化为下图,然后用BinaryNode<std::plus<double> >和BinaryNode<std:: multiplies<double> >来具现化BinaryNode<T>类模板,通过控制模板参数的类型来实现不同的运算。


在表达式树这个例子中,节点对象是动态创建的,值得思考:如何才能安全地、不重不漏地释放内存。本书第15.8节的Handle可供参考。(C++的面向对象基础设施相对于现代的语言而言显得很简陋,现在C++也不再以“支持面向对象”为卖点了。)

C++难学吗?“能够靠读书看文章读代码做练习学会的东西没什么门槛,智力正常的人只要愿意花功夫,都不难达到(不错)的程度。[36]” C++好书很多,不过优秀的C++开源代码很少,而且风格迥异[37]。我这里按个人口味和经验列几个供读者参考阅读:Google的protobuf、leveldb、PCRE的C++ 封装,我自己写的muduo网络库。这些代码都不长,功能明确,阅读难度不大。如果有时间,还可以读一读Chromium中的基础库源码。在读Google开源的C++代码时要连注释一起细读。我不建议一开始就读STL或Boost的源码,因为编写通用C++模板库和编写C++应用程序的知识体系相差很大。 另外可以考虑读一些优秀的C或Java开源项目,并思考是否可以用C++更好地实现或封装之(特别是资源管理方面能否避免手动清理)。

继续前进

我能够随手列出十几本C++好书,但是从实用角度出发,这里只举两三本必读的书。读过《C++ Primer》和这几本书之后,想必读者已能自行识别C++图书的优劣,可以根据项目需要加以钻研。

第一本是《Effective C++ 第三版》[38]。学习语法是一回事,高效地运用这门语言是另一回事。C++是一个遍布陷阱的语言,吸取专家经验尤为重要,既能快速提高眼界,又能避免重蹈覆辙。《C++ Primer》加上这本书包含的C++知识足以应付日常应用程序开发。

我假定读者一定会阅读这本书,因此在评注中不引用《Effective C++ 第三版》的任何章节。

《Effective C++ 第三版》的内容也反映了C++用法的进步。第二版建议“总是让基类拥有虚析构函数”,第三版改为“为多态基类声明虚析构函数”。因为在C++中,“继承”不光只有面向对象这一种用途,即C++的继承不一定是为了覆写(override)基类的虚函数。第二版花了很多笔墨介绍浅拷贝与深拷贝,以及对指针成员变量的处理[39]。第三版则提议,对于多数class而言,要么直接禁用拷贝构造函数和赋值操作符,要么通过选用合适的成员变量类型[40],使得编译器默认生成的这两个成员函数就能正常工作。

什么是C++编程中最重要的编程技法(idiom)?我认为是“用对象来管理资源”,即RAII。资源包括动态分配的内存[41],也包括打开的文件、TCP网络连接、数据库连接、互斥锁等等。借助RAII,我们可以把资源管理和对象生命期管理等同起来,而对象生命期管理在现代C++里根本不是困难(见注5),只需要花几天时间熟悉几个智能指针[42]的基本用法即可。学会了这三招两式,现代的C++程序中可以完全不写delete,也不必为指针或内存错误操心。现代C++程序里出现资源和内存泄漏的惟一可能是循环引用,一旦发现,也很容易修正设计和代码。这方面的详细内容请参考《Effective C++ 第三版》第3章资源管理。

C++是目前惟一能实现自动化资源管理的语言,C语言完全靠手工释放资源,而其他基于垃圾收集的语言只能自动清理内存,而不能自动清理其他资源[43](网络连接,数据库连接等等)。

除了智能指针,TR1中的bind/function也十分值得投入精力去学一学[44]。让你从一个崭新的视角,重新审视类与类之间的关系。Stephan T. Lavavej有一套PPT介绍TR1的这几个主要部件[45]

第二本书,如果读者还是在校学生,已经学过数据结构课程[46],可以考虑读一读《泛型编程与STL》[47];如果已经工作,学完《C++ Primer》立刻就要参加C++项目开发,那么我推荐阅读《C++编程规范》[48]

泛型编程有一套自己的术语,如concept、model、refinement等等,理解这套术语才能阅读泛型程序库的文档。即便不掌握泛型编程作为一种程序设计方法,也要掌握C++中以泛型思维设计出来的标准容器库和算法库(STL)。坊间面向对象的书琳琅满目,学习机会也很多,而泛型编程只有这么一本,读之可以开拓视野,并且加深对STL的理解(特别是迭代器[49])和应用。

C++模板是一种强大的抽象手段,我不赞同每个人都把精力花在钻研艰深的模板语法和技巧。从实用角度,能在应用程序中写写简单的函数模板和类模板即可(以type traits为限),不是每个人都要去写公用的模板库。

由于C++语言过于庞大复杂,我见过的开发团队都对其剪裁使用[50]。往往团队越大,项目成立时间越早,剪裁得越厉害,也越接近C。制定一份好的编程规范相当不容易。规范定得太紧(比如定为团队成员知识能力的交集),程序员束手束脚,限制了生产力,对程序员个人发展也不利[51]。规范定得太松(定为团队成员知识能力的并集),项目内代码风格迥异,学习交流协作成本上升,恐怕对生产力也不利。由两位顶级专家合写的《C++编程规范》一书可谓是现代C++编程规范的范本。

《C++编程规范》同时也是专家经验一类的书,这本书篇幅比《Effective C++ 第三版》短小,条款数目却多了近一倍,可谓言简意赅。有的条款看了就明白,照做即可:

·         第1条,以高警告级别编译代码,确保编译器无警告。

·         第31条,避免写出依赖于函数实参求值顺序的代码。C++操作符的优先级、结合性与表达式的求值顺序是无关的。裘宗燕老师写的《C/C++ 语言中表达式的求值》[52]一文对此有明确的说明。

·         第35条,避免继承“并非设计作为基类使用”的class。

·         第43条,明智地使用pimpl。这是编写C++动态链接库的必备手法,可以最大限度地提高二进制兼容性。

·         第56条,尽量提供不会失败的swap()函数。有了swap()函数,我们在自定义赋值操作符时就不必检查自赋值了。

·         第59条,不要在头文件中或#include之前写using。

·         第73条,以by value方式抛出异常,以by reference方式捕捉异常。

·         第76条,优先考虑vector,其次再选择适当的容器。

·         第79条,容器内只可存放value和smart pointer。

有的条款则需要相当的设计与编码经验才能解其中三昧:

·         第5条,为每个物体(entity)分配一个内聚任务。

·         第6条,正确性、简单性、清晰性居首。

·         第8、9条,不要过早优化;不要过早劣化。

·         第22条,将依赖关系最小化。避免循环依赖。

·         第32条,搞清楚你写的是哪一种class。明白value class、base class、trait class、policy class、exception class各有其作用,写法也不尽相同。

·         第33条,尽可能写小型class,避免写出大怪兽。

·         第37条,public继承意味着可替换性。继承非为复用,乃为被复用。

·         第57条,将class类型及其非成员函数接口放入同一个namespace。

值得一提的是,《C++编程规范》是出发点,但不是一份终极规范。例如Google的C++编程规范[53]和LLVM编程规范[54]都明确禁用异常,这跟这本书的推荐做法正好相反。

评注版使用说明

评注版采用大开本印刷,在保留原书板式的前提下,对原书进行了重新分页,评注的文字与正文左右分栏并列排版。本书已依据原书2010年第11次印刷的版本进行了全面修订。为了节省篇幅,原书每章末尾的小结和术语表还有书末的索引都没有印在评注版中,而是做成PDF供读者下载,这也方便读者检索。评注的目的是帮助初次学习C++的读者快速深入掌握这门语言的核心知识,澄清一些概念、比较与其他语言的不同、补充实践中的注意事项等等。评注的内容约占全书篇幅的15%,大致比例是三分评、七分注,并有一些补白的内容[55]。如果读者拿不定主意是否购买,可以先翻一翻第5章。我在评注中不谈C++11[56],但会略微涉及TR1,因为TR1已经投入实用。

为了不打断读者阅读的思路,评注中不会给URL链接,评注中偶尔会引用《C++编程规范》的条款,以[CCS]标明,这些条款的标题已在前文列出。另外评注中出现的soXXXXXX表示http://stackoverflow.com/questions/XXXXXX 网址。

网上资源

代码下载:http://www.informit.com/store/product.aspx?isbn=0201721481
豆瓣页面:http://book.douban.com/subject/10944985/
术语表与索引PDF下载:http://chenshuo.com/cp4/
本文电子版发布于https://github.com/chenshuo/documents/downloads/LearnCpp.pdf,方便读者访问脚注中的网站。

我的联系方式:giantchen_AT_gmail.com                    http://weibo.com/giantchen

 

陈硕

2012年3月

中国·香港

 

评注者简介 :

陈硕,北京师范大学硕士,擅长 C++ 多线程网络编程和实时分布式系统架构。现任职于香港某跨国金融公司 IT 部门,从事实时外汇交易系统开发。编写了开源 C++ 网络库 muduo; 参与翻译了《代码大全(第二版)》和《C++ 编程规范(繁体版)》;2009 年在上海 C++ 技术大会做技术演讲《当析构函数遇到多线程》,同时担任 Stanley Lippman 先生的口译员;2010 年在珠三角技术沙龙做技术演讲《分布式系统的工程化开发方法》;2012年在“我们的开源项目”深圳站做《Muduo 网络库:现代非阻塞C++网络编程》演讲。

 



[1] 见编程语言性能对比网站 http://shootout.alioth.debian.org/ 和Google 员工写的语言性能对比论文

   https://days2011.scala-lang.org/sites/days2011/files/ws3-1-Hundt.pdf

[2] C++之父Bjarne Stroustrup维护的C++用户列表:http://www2.research.att.com/~bs/applications.html

[3] 初窥C++在嵌入式系统中的应用,请见http://aristeia.com/TalkNotes/MISRA_Day_2010.pdf

[4] Milo Yip在《C++强大背后》提到大部分游戏引擎(如Unreal/Source)及中间件(如Havok/FMOD)是C++实现的。http://www.cnblogs.com/miloyip/archive/2010/09/17/behind_cplusplus.html

[5] 孟岩《垃圾收集机制批判》:C++利用智能指针达成的效果是,一旦某对象不再被引用,系统刻不容缓,立刻回收内存。这通常发生在关键任务完成后的清理(clean up)时期,不会影响关键任务的实时性,同时,内存里所有的对象都是有用的,绝对没有垃圾空占内存。http://blog.csdn.net/myan/article/details/1906

[6] 有人半开玩笑地说“所谓系统编程,就是那些CPU时间比程序员的时间更重要的工作。”

[7] 《Software Development for Infrastructure》 http://www2.research.att.com/~bs/Computer-Jan12.pdf

[8] Herb Sutter在C++ and Beyond 2011会议上的开场演讲《Why C++?》
   http://channel9.msdn.com/posts/C-and-Beyond-2011-Herb-Sutter-Why-C

[9] 这里的灵活性指的是编译器不阻止你干你想干的事情,比如为了追求运行效率而实现即时编译(just-in-time compilation)。

[10] 我曾向Stan Lippman介绍目前我在Linux下的工作环境(编辑器、编译器、调试器),他表示这跟他在1970年代的工作环境相差无几,可见C++在开发工具方面的落后。另外C++的编译运行调试周期也比现代的语言长,这多少影响了工作效率。

[11] 可参考Ulrich Drepper在《Stop Underutilizing Your Computer》中举的SIMD例子。
   http://www.redhat.com/f/pdf/summit/udrepper_945_stop_underutilizing.pdf

[12]《Technical Report on C++ Performance》 http://www.open-std.org/jtc1/sc22/wg21/docs/18015.html

[13] 可参考Scott Meyers的《Effective C++ in an Embedded Environment》

   http://www.artima.com/shop/effective_cpp_in_an_embedded_environment

[14] 我们知道std::list的任一位置插入是O(1)操作,而std::vector的任一位置插入是O(N)操作,但由于std::vector的元素布局更加紧凑(compact),很多时候std::vector的随机插入性能甚至会高于std::list。见http://ecn.channel9.msdn.com/events/GoingNative12/GN12Cpp11Style.pdf 这也佐证std::vector是首选容器。

[15] 可参考Scott Meyers的技术报告《CPU Caches and Why You Care》和任何一本现代的计算机体系结构教材 http://aristeia.com/TalkNotes/ACCU2011_CPUCaches.pdf

[16] Bjarne Stroustrup有一篇论文《Abstraction and the C++ machine model》对比了C++和Java的对象内存布局。 http://www2.research.att.com/~bs/abstraction-and-machine.pdf

[17] 语出孟岩《快速掌握一个语言最常用的50%》 http://blog.csdn.net/myan/article/details/3144661

[18] 同样篇幅的Java/C#/Python教材可以从语言、标准库一路讲到多线程、网络编程、图形编程。

[19] “权威”的意思是说你不用担心作者讲错了,能达到这个水准的C++图书作者全世界也屈指可数。

[20] 侯捷《大道之行也——C++ Primer 3/e译序》 http://jjhou.boolan.com/cpp-primer-foreword.pdf

[21] Bjarne Stroustrup在《Programming--- Principles and Practice Using C++》的参考文献中引用了本书,并特别注明 use only the 4th edition.

[22] 侯捷《C++ Primer 4/e译序》

[23] 如果没有时间精读注21中提到的那本大部头,短小精干的《Accelerated C++》亦是上佳之选。另外如果想从C语言入手,我推荐裘宗燕老师的《从问题到程序:程序设计与C语言引论(第2版)》

[24] 本书把iostream的格式化输出放到附录,彻底不谈locale/facet,可谓匠心独运。

[25] Stan Lippman曾说:Virtual base class support wanders off into the Byzantine... The material is simply too esoteric to warrant discussion...

[26] 基本等同于1998年的初版C++标准,修正了编译器作者关心的一些问题,与普通程序员基本无关。

[27] TR1是2005年C++标准库的一次扩充,增加了智能指针、bind/function、哈希表、正则表达式等。

[28]作者正在编写《C++ Primer 第五版》,会包含C++11的内容。

[29] G++统治了Linux平台,并且能用在很多Unix平台上;Visual C++统治了Windows平台。其他C++编译器的行为通常要向它们靠拢,例如Intel C++在Linux上要兼容G++,而在Windows上要兼容Visual C++。

[30] 曾经是Cfront,本书作者正是其主要开发者。http://www.softwarepreservation.org/projects/c_plus_plus

[31] 包括C++标准有规定,但编译器拒绝遵循的。http://stackoverflow.com/questions/3931312/value-initialization-and-non-pod-types

[32] G++ 是免费的,可使用较新的4.x版,最好32-bit和64-bit一起用,因为服务端已经普及64位。微软也有免费的编译器,可考虑Visual C++ 2010 Express,建议不要用老掉牙的Visual C++ 6.0作为学习平台。

[33] 可参考陈硕写的《C++工程实践经验谈》中的“C++编译模型精要”一节。

[34] 大整数类可以以std::vector<int>为成员变量,避免手动资源管理。

[35] “解析”可以用数据结构课程介绍的逆波兰表达式方法,也可以用编译原理中介绍的递归下降法,还可以用专门的Packrat算法。可参考http://www.relisoft.com/book/lang/poly/3tree.html

[36]  孟岩《技术路线的选择重要但不具有决定性》 http://blog.csdn.net/myan/article/details/3247071

[37] 从代码风格上往往能判断项目成型的时代。

[38] Scott Meyers著,侯捷译,电子工业出版社。

[39] Andrew Koenig的《Teaching C++ Badly: Introduce Constructors and Destructors at the Same Time》

http://drdobbs.com/blogs/cpp/229500116

[40] 能自动管理资源的string、vector、shared_ptr等等,这样多数class连析构函数都不必写。

[41] “分配内存”包括在堆(heap)上创建对象。

[42] 包括TR1中的shared_ptr、weak_ptr,还有更简单的boost::scoped_ptr。

[43] Java 7有try-with-resources语句,Python有with语句,C#有using语句,可以自动清理栈上的资源,但对生命期大于局部作用域的资源无能为力,需要程序员手工管理。

[44] 孟岩《function/bind的救赎(上)》http://blog.csdn.net/myan/article/details/5928531

[45] http://blogs.msdn.com/b/vcblog/archive/2008/02/22/tr1-slide-decks.aspx

[46] 最好再学一点基础的离散数学。

[47] Matthew Austern著,侯捷译,中国电力出版社。

[48] Herb Sutter等著,刘基诚译,人民邮电出版社。(这本书繁体版由侯捷先生和我翻译。)

[49] 侯捷先生的《芝麻开门:从Iterator谈起》 http://jjhou.boolan.com/programmer-3-traits.pdf

[50] 孟岩《编程语言的层次观点——兼谈C++的剪裁方案》 http://blog.csdn.net/myan/article/details/1920

[51] 一个人通常不会在一个团队工作一辈子,其他团队可能有不同的C++剪裁使用方式,程序员要有“一桶水”的本事,才能应付不同形状大小的水碗。

[52] http://www.math.pku.edu.cn/teachers/qiuzy/technotes/expression2009.pdf

[53] http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Exceptions

[54] http://llvm.org/docs/CodingStandards.html#ci_rtti_exceptions

[55] 第10章绘制了数据结构示意图,第11章补充lower_bound 和 upper_bound的示例。

[56] 从Scott Meyers的讲义可以快速学习C++11 http://www.artima.com/shop/overview_of_the_new_cpp


评论 1 您还未登录,请先 登录 后发表或查看评论
【原书名】 C++ Primer (4th Edition) 【原出版社】 Addison Wesley/Pearson 【作者】 (美)Stanley B.Lippman,Josée LaJoie,Barbara E.Moo 【译者】 李师贤 蒋爱军 梅晓勇 林瑛 【丛书名】 图灵计算机科学丛书 【出版社】 人民邮电出版社 【书号】 7-115-14554-7 【开本】 16开 【页码】 900 【出版日期】 2006-3-1 【版次】 4-1 【内容简介】 本书是久负盛名的C++经典教程,其内容是C++大师Stanley B. Lippman丰富的实践经验和C++标准委员会原负责人Josée Lajoie对C++标准深入理解的完美结合,已经帮助全球无数程序员学会了C++。本版对前一版进行了彻底的修订,内容经过了重新组织,更加入了C++ 先驱Barbara E. Moo在C++教学方面的真知灼见。既显著改善了可读性,又充分体现了C++语言的最新进展和当前的业界最佳实践。书中不但新增大量教学辅助内容,用于强调重要的知识点,提醒常见的错误,推荐优秀的编程实践,给出使用提示,还包含大量来自实战的示例和习题。对C++基本概念和技术全面而且权威的阐述,对现代C++编程风格的强调,使本书成为C++初学者的最佳指南;对于中高级程序员,本书也是不可或缺的参考书。本书的前言阐述了 第4版和前一版的不同之处。 【目录信息】 第1章 快速入门 1 1.1 编写简单的C++程序 2 1.2 初窥输入/输出 5 1.2.1 标准输入与输出对象 5 1.2.2 一个使用IO库的程序 5 1.3 关于注释 8 1.4 控制结构 10 1.4.1 while语句 10 1.4.2 for语句 12 1.4.3 if语句 14 1.4.4 读入未知数目的输入 15 1.5 类的简介 17 1.5.1 Sales_item类 17 1.5.2 初窥成员函数 19 1.6 C++程序 21 小结 22 术语 22 第一部分 基本语言 第2章 变量和基本类型 29 2.1 基本内置类型 30 2.1.1 整型 30 2.1.2 浮点型 32 2.2 字面值常量 34 2.3 变量 38 2.3.1 什么是变量 39 2.3.2 变量名 40 2.3.3 定义对象 42 2.3.4 变量初始化规则 44 2.3.5 声明和定义 45 2.3.6 名字的作用域 46 2.3.7 在变量使用处定义变量 48 2.4 const限定符 49 2.5 引用 50 2.6 typedef名字 53 2.7 枚举 53 2.8 类类型 54 2.9 编写自己的头文件 57 2.9.1 设计自己的头文件 58 2.9.2 预处理器的简单介绍 60 小结 62 术语 62 第3章 标准库类型 67 3.1 命名空间的using声明 68 3.2 标准库string类型 70 3.2.1 string对象的定义和初始化 70 3.2.2 String对象的读写 71 3.2.3 string对象的操作 72 3.2.4 string对象中字符的处理 76 3.3 标准库vector类型 78 3.3.1 vector对象的定义和初始化 79 3.3.2 vector对象的操作 81 3.4 迭代器简介 83 3.5 标准库bitset类型 88 3.5.1 bitset对象的定义和初始化 88 3.5.2 bitset对象上的操作 90 小结 92 术语 92 第4章 数组和指针 95 4.1 数组 96 4.1.1 数组的定义和初始化 96 4.1.2 数组操作 99 4.2 指针的引入 100 4.2.1 什么是指针 100 4.2.2 指针的定义和初始化 101 4.2.3 指针操作 104 4.2.4 使用指针访问数组元素 106 4.2.5 指针和const限定符 110 4.3 C风格字符串 113 4.3.1 创建动态数组 117 4.3.2 新旧代码的兼容 120 4.4 多维数组 122 小结 124 术语 125 第5章 表达式 127 5.1 算术操作符 129 5.2 关系操作符和逻辑操作符 131 5.3 位操作符 134 5.3.1 bitset对象或整型值的使用 135 5.3.2 将移位操作符用于IO 137 5.4 赋值操作符 137 5.4.1 赋值操作的右结合性 138 5.4.2 赋值操作具有低优先级 138 5.4.3 复合赋值操作符 139 5.5 自增和自减操作符 140 5.6 箭头操作符 142 5.7 条件操作符 143 5.8 sizeof操作符 144 5.9 逗号操作符 145 5.10 复合表达式的求值 145 5.10.1 优先级 145 5.10.2 结合性 146 5.10.3 求值顺序 148 5.11 new和delete表达式 150 5.12 类型转换 154 5.12.1 何时发生隐式类型转换 154 5.12.2 算术转换 155 5.12.3 其他隐式转换 156 5.12.4 显式转换 158 5.12.5 何时需要强制类型转换 158 5.12.6 命名的强制类型转换 158 5.12.7 旧式强制类型转换 160 小结 161 术语 162 第6章 语句 165 6.1 简单语句 166 6.2 声明语句 167 6.3 复合语句(块) 167 6.4 语句作用域 168 6.5 if语句 169 6.6 switch语句 172 6.6.1 使用switch 173 6.6.2 switch中的控制流 173 6.6.3 default标号 175 6.6.4 switch表达式与case标号 176 6.6.5 switch内部的变量定义 176 6.7 while语句 177 6.8 for循环语句 179 6.8.1 省略for语句头的某些部分 180 6.8.2 for语句头中的多个定义 181 6.9 do while语句 182 6.10 break语句 183 6.11 continue语句 184 6.12 goto语句 185 6.13 try块和异常处理 186 6.13.1 throw表达式 186 6.13.2 try块 187 6.13.3 标准异常 189 6.14 使用预处理器进行调试 190 小结 192 术语 192 第7章 函数 195 7.1 函数的定义 196 7.1.1 函数返回类型 197 7.1.2 函数形参表 198 7.2 参数传递 199 7.2.1 非引用形参 199 7.2.2 引用形参 201 7.2.3 vector和其他容器类型的形参 206 7.2.4 数组形参 206 7.2.5 传递给函数的数组的处理 209 7.2.6 main:处理命令行选项 210 7.2.7 含有可变形参的函数 211 7.3 return语句 211 7.3.1 没有返回值的函数 212 7.3.2 具有返回值的函数 212 7.3.3 递归 216 7.4 函数声明 217 7.5 局部对象 220 7.5.1 自动对象 220 7.5.2 静态局部对象 220 7.6 内联函数 221 7.7 类的成员函数 222 7.7.1 定义成员函数的函数体 223 7.7.2 在类外定义成员函数 225 7.7.3 编写Sales_item类的构造 函数 225 7.7.4 类代码文件的组织 227 7.8 重载函数 228 7.8.1 重载与作用域 230 7.8.2 函数匹配与实参转换 231 7.8.3 重载确定的三个步骤 232 7.8.4 实参类型转换 234 7.9 指向函数的指针 237 小结 239 术语 240 第8章 标准IO库 243 8.1 面向对象的标准库 244 8.2 条件状态 247 8.3 输出缓冲区的管理 249 8.4 文件的输入和输出 251 8.4.1 文件流对象的使用 251 8.4.2 文件模式 254 8.4.3 一个打开并检查输入文件的 程序 256 8.5 字符串流 257 小结 259 术语 259 第二部分 容器和算法 第9章 顺序容器 263 9.1 顺序容器的定义 264 9.1.1 容器元素的初始化 265 9.1.2 容器内元素的类型约束 267 9.2 迭代器和迭代器范围 268 9.2.1 迭代器范围 270 9.2.2 使迭代器失效的容器操作 271 9.3 顺序容器的操作 272 9.3.1 容器定义的类型别名 272 9.3.2 begin和end成员 273 9.3.3 在顺序容器中添加元素 273 9.3.4 关系操作符 277 9.3.5 容器大小的操作 278 9.3.6 访问元素 279 9.3.7 删除元素 280 9.3.8 赋值与swap 282 9.4 vector容器的自增长 284 9.5 容器的选用 287 9.6 再谈string类型 289 9.6.1 构造string对象的其他方法 290 9.6.2 修改string对象的其他方法 292 9.6.3 只适用于string类型的操作 293 9.6.4 string类型的查找操作 295 9.6.5 string对象的比较 298 9.7 容器适配器 300 9.7.1 栈适配器 301 9.7.2 队列和优先级队列 302 小结 303 术语 303 第10章 关联容器 305 10.1 引言:pair类型 306 10.2 关联容器 308 10.3 map类型 309 10.3.1 map对象的定义 309 10.3.2 map定义的类型 310 10.3.3 给map添加元素 311 10.3.4 使用下标访问map对象 311 10.3.5 map::insert的使用 313 10.3.6 查找并读取map中的元素 315 10.3.7 map对象中删除元素 316 10.3.8 map对象的迭代遍历 316 10.3.9 “单词转换”map对象 317 10.4 set类型 319 10.4.1 set容器的定义和使用 319 10.4.2 创建“单词排除”集 321 10.5 multimap和multiset类型 322 10.5.1 元素的添加和删除 322 10.5.2 在multimap和multiset 中查找元素 323 10.6 容器的综合应用:文本查询程序 325 10.6.1 查询程序的设计 326 10.6.2 TextQuery类 327 10.6.3 TextQuery类的使用 328 10.6.4 编写成员函数 330 小结 332 术语 332 第11章 泛型算法 335 11.1 概述 336 11.2 初窥算法 339 11.2.1 只读算法 339 11.2.2 写容器元素的算法 341 11.2.3 对容器元素重新排序的算法 343 11.3 再谈迭代器 347 11.3.1 插入迭代器 348 11.3.2 iostream迭代器 349 11.3.3 反向迭代器 353 11.3.4 const迭代器 355 11.3.5 五种迭代器 356 11.4 泛型算法的结构 358 11.4.1 算法的形参模式 359 11.4.2 算法的命名规范 359 11.5 容器特有的算法 361 小结 362 术语 363 第三部分 类和数据抽象 第12章 类 367 12.1 类的定义和声明 368 12.1.1 类定义:扼要重述 368 12.1.2 数据抽象和封装 369 12.1.3 关于类定义的更多内容 372 12.1.4 类声明与类定义 374 12.1.5 类对象 375 12.2 隐含的this指针 376 12.3 类作用域 380 类作用域中的名字查找 382 12.4 构造函数 385 12.4.1 构造函数初始化式 387 12.4.2 默认实参与构造函数 391 12.4.3 默认构造函数 392 12.4.4 隐式类类型转换 393 12.4.5 类成员的显式初始化 396 12.5 友元 396 12.6 static类成员 398 12.6.1 static成员函数 400 12.6.2 static数据成员 400 小结 403 术语 403 第13章 复制控制 405 13.1 复制构造函数 406 13.1.1 合成的复制构造函数 409 13.1.2 定义自己的复制构造函数 409 13.1.3 禁止复制 410 13.2 赋值操作符 411 13.3 析构函数 412 13.4 消息处理示例 415 13.5 管理指针成员 419 13.5.1 定义智能指针类 421 13.5.2 定义值型类 425 小结 427 术语 427 第14章 重载操作符与转换 429 14.1 重载操作符的定义 430 14.2 输入和输出操作符 435 14.2.1 输出操作符<>的重载 437 14.3 算术操作符和关系操作符 439 14.3.1 相等操作符 440 14.3.2 关系操作符 441 14.4 赋值操作符 441 14.5 下标操作符 442 14.6 成员访问操作符 443 14.7 自增操作符和自减操作符 446 14.8 调用操作符和函数对象 449 14.8.1 将函数对象用于标准库算法 450 14.8.2 标准库定义的函数对象 451 14.8.3 函数对象的函数适配器 453 14.9 转换与类类型 454 14.9.1 转换为什么有用 454 14.9.2 转换操作符 455 14.9.3 实参匹配和转换 458 14.9.4 重载确定和类的实参 461 14.9.5 重载、转换和操作符 464 小结 466 术语 467 第四部分 面向对象编程与泛型编程 第15章 面向对象编程 471 15.1 面向对象编程:概述 472 15.2 定义基类和派生类 473 15.2.1 定义基类 474 15.2.2 protected成员 475 15.2.3 派生类 476 15.2.4 virtual与其他成员函数 479 15.2.5 公用、私有和受保护的继承 482 15.2.6 友元关系与继承 486 15.2.7 继承与静态成员 486 15.3 转换与继承 487 15.3.1 派生类到基类的转换 487 15.3.2 基类到派生类的转换 489 15.4 构造函数和复制控制 490 15.4.1 基类构造函数和复制控制 490 15.4.2 派生类构造函数 490 15.4.3 复制控制和继承 494 15.4.4 虚析构函数 495 15.4.5 构造函数和析构函数中的虚函数 497 15.5 继承情况下的类作用域 497 15.5.1 名字查找在编译时发生 498 15.5.2 名字冲突与继承 498 15.5.3 作用域与成员函数 499 15.5.4 虚函数与作用域 500 15.6 纯虚函数 502 15.7 容器与继承 503 15.8 句柄类与继承 504 15.8.1 指针型句柄 505 15.8.2 复制未知类型 507 15.8.3 句柄的使用 508 15.9 再谈文本查询示例 511 15.9.1 面向对象的解决方案 513 15.9.2 值型句柄 514 15.9.3 Query_base类 515 15.9.4 Query句柄类 516 15.9.5 派生类 518 15.9.6 eval函数 520 小结 522 术语 523 第16章 模板与泛型编程 525 16.1 模板定义 526 16.1.1 定义函数模板 526 16.1.2 定义类模板 528 16.1.3 模板形参 529 16.1.4 模板类型形参 531 16.1.5 非类型模板形参 533 16.1.6 编写泛型程序 534 16.2 实例化 535 16.2.1 模板实参推断 537 16.2.2 函数模板的显式实参 540 16.3 模板编译模型 542 16.4 类模板成员 545 16.4.1 类模板成员函数 548 16.4.2 非类型形参的模板实参 551 16.4.3 类模板中的友元声明 552 16.4.4 Queue和QueueItem的友元 声明 554 16.4.5 成员模板 556 16.4.6 完整的Queue类 558 16.4.7 类模板的static成员 559 16.5 一个泛型句柄类 560 16.5.1 定义句柄类 561 16.5.2 使用句柄 562 16.6 模板特化 564 16.6.1 函数模板的特化 565 16.6.2 类模板的特化 567 16.6.3 特化成员而不特化类 569 16.6.4 类模板的部分特化 570 16.7 重载与函数模板 570 小结 573 术语 574 第五部分 高级主题 第17章 用于大型程序的工具 579 17.1 异常处理 580 17.1.1 抛出类类型的异常 581 17.1.2 栈展开 582 17.1.3 捕获异常 583 17.1.4 重新抛出 585 17.1.5 捕获所有异常的处理代码 586 17.1.6 函数测试块与构造函数 586 17.1.7 异常类层次 587 17.1.8 自动资源释放 589 17.1.9 auto_ptr类 591 17.1.10 异常说明 595 17.1.11 函数指针的异常说明 598 17.2 命名空间 599 17.2.1 命名空间的定义 599 17.2.2 嵌套命名空间 603 17.2.3 未命名的命名空间 604 17.2.4 命名空间成员的使用 606 17.2.5 类、命名空间和作用域 609 17.2.6 重载与命名空间 612 17.2.7 命名空间与模板 614 17.3 多重继承与虚继承 614 17.3.1 多重继承 615 17.3.2 转换与多个基类 617 17.3.3 多重继承派生类的复制控制 619 17.3.4 多重继承下的类作用域 620 17.3.5 虚继承 622 17.3.6 虚基类的声明 624 17.3.7 特殊的初始化语义 625 小结 628 术语 628 第18章 特殊工具与技术 631 18.1 优化内存分配 632 18.1.1 C++中的内存分配 632 18.1.2 allocator类 633 18.1.3 operator new函数和 operator delete函数 636 18.1.4 定位new表达式 638 18.1.5 显式析构函数的调用 639 18.1.6 类特定的new和delete 639 18.1.7 一个内存分配器基类 641 18.2 运行时类型识别 646 18.2.1 dynamic_cast操作符 647 18.2.2 typeid操作符 649 18.2.3 RTTI的使用 650 18.2.4 type_info类 652 18.3 类成员的指针 653 18.3.1 声明成员指针 653 18.3.2 使用类成员的指针 655 18.4 嵌套类 658 18.4.1 嵌套类的实现 658 18.4.2 嵌套类作用域中的名字查找 661 18.5 联合:节省空间的类 662 18.6 局部类 665 18.7 固有的不可移植的特征 666 18.7.1 位域 666 18.7.2 volatile限定符 668 18.7.3 链接指示:extern "C" 669 小结 672 术语 673 附录 标准库 675 索引 703
有人说C++程序员可以分成两类,读过Effective C++的和没读过的。世界顶级C++大师Scott Meyers成名之作的第三版的确当得起这样的评价。当您读过这本书之后,就获得了迅速提升自己C++功力的一个契机。 《Effective C++一共组织 55 个准则,每一条准则描述一个编写出更好的 C++ 的方式。每一个条款的背后都有具体范例支撑。第三版有一半以上的篇幅是崭新内容,包括讨论资源管理和模板(templates)运用的两个新章。为反映出现代设计考虑,对第二版论题做了广泛的修订,包括异常(exceptions)、设计模式(design patterns)和多线程(multithreading)。 《Effective C++的重要特征包括:   * 高效的 classes、functions、templates 和inheritance hierarchies(继承体系)方面的专家级指导。   * 崭新的 "TR1" 标准程序库功能应用,以及与既有标准程序库组件的比较。   * 洞察 C++和其他语言(例如Java、C#、C)之间的不同。此举有助于那些来自其他语言阵营的开发人员消化吸收 C++ 式的各种解法。 《More Effective C++所给出的建议都是源于日常编程实践的一些真知灼见。和它的姊妹篇《Effective C++一样,本书是每个C++开发者必不可少的书籍。   作者Meyers在本书中为我们呈现了35种用于提升程序和设计的新方法。根据多年的经验,Meyers解释了如何才能编写更高效的C++软件:运行效率更高、更为健壮、更为稳定、更容易移植以及更易于重用。简而言之,就是如何编写更好的C++软件。   一些经过实践检验的用来改善程序效率的方法,包括检验C++语言特性所带来的时间和空间上的开销。   全面地描述了C++专家所使用的高级技术,包括placement new、虚构造函数、智能指针、引用计数、代理类和双重分派等。   本书主要特点:以实例说明异常处理结构上及行为上给C++类和函数带来的巨大影响。   实用的角度介绍新的语言特性,包括bool、mutable、explicit、namespaces、成员模板以及标准模板库等。如果你的编译器还不支持这些新特性,Meyers会告诉你如何使用其他的方法来达到同样的效果。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
©️2022 CSDN 皮肤主题:大白 设计师:CSDN官方博客 返回首页

打赏作者

陈硕

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值