---------------------------C++程序设计:原理与实践(基础篇)(原书第2版)---------------------------
当实际地形与地图不符时,相信实际地形。
——瑞士军队谚语
讲授和学习本书的方法
我们是如何帮助你学习的?又是如何安排学习进程的?我们的做法是,尽力为你提供编写高效的实用程序所需的最基本的概念、技术和工具,包括程序组织、调试和测试、类设计、计算、函数和算法设计、绘图方法(仅介绍二维图形)、图形用户界面(GUI)、文本处理、正则表达式匹配、文件和流输入输出(I/O)、内存管理、科学/数值/工程计算、设计和编程思想、C++标准库、软件开发策略、C语言程序设计技术。认真完成这些内容的学习,我们会学到如下程序设计技术:过程式程序设计(C语言程序设计风格)、数据抽象、面向对象程序设计和泛型程序设计。本书的主题是程序设计,也就是表达代码意图所需的思想、技术和工具。C++语言是我们的主要工具,因此我们比较详细地描述了很多C++语言的特性。但请记住,C++只是一种工具,而不是本书的主题。本书是“用C++语言进行程序设计”,而不是“C++和一点程序设计理论”。
我们介绍的每个主题都至少出于两个目的:提出一种技术、概念或原理,介绍一个实用的语言特性或库特性。例如,我们用一个二维图形绘制系统的接口展示如何使用类和继承。这使我们节省了篇幅(也节省了你的时间),并且还强调了程序设计不只是简单地将代码拼装起来以尽快地得到一个结果。C++标准库是这种“双重作用”例子的主要来源,其中很多主题甚至具有三重作用。例如,我们会介绍标准库中的向量类vector,用它来展示一些广泛使用的设计技术,并展示很多用来实现vector的程序设计技术。我们的一个目标是向你展示一些主要的标准库功能是如何实现的,以及它们如何与硬件相配合。我们坚持认为一个工匠必须了解他的工具,而不是仅仅把工具当作“有魔力的东西”。
对于一个程序员来说,总是会对某些主题比对其他主题更感兴趣。但是,我们建议你不要预先判断你需要什么(你怎么知道你将来会需要什么呢?),至少每一章都要浏览一下。如果你学习本书是作为一门课程的一部分,你的老师会指导你如何选择学习内容。
我们的教学方法可以描述为“深度优先”,同时也是“具体优先”和“基于概念”。首先,我们快速地(好吧,是相对快速地,从第1章到第11章)将一些编写小的实用程序所需的技巧提供给你。在这期间,我们还简明扼要地提出很多工具和技术。我们着重于简单具体的代码实例,因为相对于抽象概念,人们能更快领会具体实例,这就是多数人的学习方法。在最初阶段,你不应期望理解每个小的细节。特别是,你会发现对刚刚还工作得好好的程序稍加改动,便会呈现出“神秘”的效果。尽管如此,你还是要尝试一下!还有,请完成我们提供的简单练习和习题。请记住,在学习初期你只是没有掌握足够的概念和技巧来准确判断什么是简单的,什么是复杂的。请等待一些惊奇的事情发生,并从中学习吧。
我们会快速通过这样一个初始阶段——我们想尽可能快地带你进入编写有趣程序的阶段。有些人可能会质疑,“我们的进展应该慢些、谨慎些,我们应该先学会走,再学跑!”但是你见过小孩学习走路吗?实际上小孩在学会平稳地慢慢走路之前就开始尝试跑了。与之相似,你可以先勇猛向前,偶尔摔一跤,从中获得编程的感觉,然后再慢下来,获得必要的精确控制能力和准确的理解。你必须在学会走之前就开始跑!
你不要投入大量精力试图学习一些语言或技术细节的所有相关内容。例如,你可以熟记所有C++的内置类型及其使用规则。你当然可以这么做,而且这么做会使你觉得自己很博学。但是,这不会使你成为一名程序员。如果你学习中略过一些细节,将来可能偶尔会因为缺少相关知识而被“灼伤”,但这是获取编写好程序所需的完整知识结构的最快途径。注意,我们的这种方法本质上就是小孩学习其母语的方法,也是教授外语的最有效方法。有时你不可避免地被难题困住,我们鼓励你向授课老师、朋友、同事、指导教师等寻求帮助。请放心,在前面这些章节中,所有内容本质上都不困难。但是,很多内容是你所不熟悉的,因此最初可能会感觉有点难。
随后,我们介绍一些入门技巧来拓宽你的知识。我们通过实例和习题来强化你的理解,为你提供一个程序设计的概念基础。
我们非常强调思想和原理。思想能指导你求解实际问题——可以帮助你知道在什么情况下问题求解方案是好的、合理的。你还应该理解这些思想背后的原理,从而理解为什么要接受这些思想,为什么遵循这些思想会对你和使用你的代码的用户有帮助。没有人会满意“因为事情就是如此”这样的解释。更为重要的是,如果真正理解了思想和原理,你就能将自己已知的知识推广到新的情况;就能用新的方法将思想和工具结合来解决新的问题。知其所以然是学会程序设计技巧所必需的。相反,仅仅不求甚解地记住大量规则和语言特性有很大局限,是错误之源,是在浪费时间。我们认为你的时间很珍贵,尽量不要浪费它。
我们把很多C++语言层面的技术细节放在了附录和手册中,你可以随时按需查找。我们假定你有能力查找到需要的信息,你可以借助目录来查找信息。不要忘了编译器和互联网的在线功能。但要记住,要对所有互联网资源保持足够的怀疑,直至你有足够的理由相信它们。因为很多看起来很权威的网站实际上是由程序设计新手或者想要出售什么东西的人建立的。而另外一些网站,其内容都是过时的。我们在支持网站www.stroustrup.com/Programming上列出了一些有用的网站链接和信息。
请不要过于急切地期盼“实际的”例子。我们理想的实例都是能直接说明一种语言特性、一个概念或者一种技术的简短代码。很多现实世界中的实例比我们给出的实例要凌乱很多,而且所能展示的知识也不比我们的实例更多。包含数十万行代码的成功商业程序中所采用的技术,我们用几个50行规模的程序就能展示出来。理解现实世界程序的最快途径是好好研究一些基础的小程序。
另一方面,我们不会用“聪明可爱的风格”来阐述我们的观点。我们假定你的目标是编写供他人使用的实用程序,因此书中给出的实例要么是用来说明语言特性,要么是从实际应用中提取出来的。我们的叙述风格都是用专业人员对(将来的)专业人员的那种口气。
. 一般方法
本书的内容组织适合从头到尾一章一章地阅读,当然,你也常常要回过头来对某些内容读上第二遍、第三遍。实际上,这是一种明智的方法,因为当遇到还看不出什么门道的地方时,你通常会快速掠过。对于这种情况,你最终还是会再次回到这个地方。然而,这么做要适度,因为除了交叉引用之外,对本书其他部分,你随便翻开一页,就从那里开始学习,并希望成功,是不可能的。本书每一节、每一章的内容安排,都假定你已经理解了之前的内容。
本书的每一章都是一个合理的自包含单元,这意味着应一口气读完(当然这只是理论上,实际上由于学生紧密的学习计划,不总是可行)。这是将内容划分为章的主要标准。其他标准包括:从简单练习和习题的角度,每章是一个合适的单元;每一章提出一些特定的概念、思想或技术。这种标准的多样性使得少数章过长,所以不要教条地遵循“一口气读完”的准则。特别是当你已经考虑了思考题,做了简单练习,并做了一些习题时,你通常会发现你需要回过头去重读一些小节和几天前读过的内容。
“它回答了我想到的所有问题”是对一本教材常见的称赞,这对细节技术问题是很理想的,而早期的读者也发现本书有这样的特性。但是,这不是全部的理想,我们希望提出初学者可能想不到的问题。我们的目标是,回答那些你在编写供他人使用的高质量软件时需要考虑的问题。学习回答好的(通常也是困难的)问题是学习如何像一个程序员那样思考所必需的。只回答那些简单的、浅显的问题会使你感觉良好,但无助于你成长为一名程序员。
我们努力尊重你的智力,珍惜你的时间。在本书中,我们以专业性而不是精明伶俐为目标,宁可有节制地表达一个观点而不大肆渲染它。我们尽力不夸大一种程序设计技术或一个语言特性的重要性,但请不要因此低估“这通常是有用的”这种简单陈述的重要程度。如果我们平静地强调某些内容是重要的,意思是你如果不掌握它,或早或晚都会因此而浪费时间。
我们不会伪称本书中的思想和工具是完美的。实际上没有任何一种工具、库、语言或者技术能够解决程序员所面临的所有难题,至多能帮助你开发、表达你的问题求解方案而已。我们尽量避免“无害的谎言”,也就是说,我们会尽力避免过于简单的解释,虽然这些解释清晰且易理解,但在实际编程和问题求解时却容易弄错。另一方面,本书不是一本参考手册,如果需要C++详细完整的描述,请参考Bjarne Stroustrup的《The C++ Programming Language》第4版(Addison-Wesley出版社,2013年)和ISO的C++标准。
简单练习和习题等
程序设计不仅仅是一种脑力活动,实际动手编写程序是掌握程序设计技巧必不可少的一环。本书提供两个层次的程序设计练习:
简单练习:简单练习是一种非常简单的习题,其目的是帮助学生掌握一些相对死板的实际编程技巧。一个简单练习通常由一系列的单个程序修改练习组成。你应该完成所有简单练习。完成简单练习不需要很强的理解能力、很聪明或者很有创造性。简单练习是本书的基本组成部分,如果你没有完成简单练习,就不能说完成了本书的学习。
习题:有些习题比较简单,有些则很难,但多数习题都是想给学生留下一定的创造和想象空间。如果时间紧张,你可以做少量习题,但题量至少应该能使你弄清楚哪些内容对你来说比较困难,在此基础上应该再多做一些,这是你的成功之道。我们希望本书的习题都是学生能够做出来的,而不是需要超乎常人的智力才能解答的复杂难题。但是,我们还是期望本书习题能给你足够多的挑战,能用光甚至是最好的学生的所有时间。我们不期待你能完成所有习题,但请尽情尝试。
另外,我们建议每个学生都能参与到一个小的项目中去(如果时间允许,能参与更多项目当然就更好了)。一个项目的目的就是要编写一个完整的有用程序。理想情况下,一个项目由一个多人小组(比如三个人)共同完成。大多数人会发现做项目非常有趣,并在这个过程中学会如何把很多事情组织在一起。
一些人喜欢在读完一章之前就把书扔到一边,开始尝试做一些实例程序;另一些人则喜欢把一章读完后,再开始编码。为了帮助前一种读者,我们用“试一试”板块给出了对于编程实践的一些简单建议。一个“试一试”通常来说就是一个简单练习,而且只着眼于前面刚刚介绍的主题。如果你略过了一个“试一试”而没有去尝试它,那么最好在做这一章的简单练习时做一下这个题目。“试一试”要么是该章简单练习的补充,要么干脆就是其中的一部分。
在每章末尾你都会看到一些思考题,我们设置这些思考题是想为你指出这一章中的重点内容。一种学习思考题的方法是把它们作为习题的补充:习题关注程序设计的实践层面,而思考题则试图帮你强化思想和概念。因此,思考题有点像面试题。
每章最后都有“术语”一节,给出本章中提出的程序设计或C++方面的基本词汇表。如果你希望理解别人关于程序设计的陈述,或者想明确表达出自己的思想,就应该首先弄清术语表中每个术语的含义。
重复是学习的有效手段,我们希望每个重要的知识点都在书中至少出现两次,并通过习题再次强调。
进阶学习
当你完成本书的学习时,是否能成为一名程序设计和C++方面的专家呢?答案当然是否定的!如果做得好的话,程序设计会是一门建立在多种专业技能上的精妙的、深刻的、需要高度技巧的艺术。你不能期望花四个月时间就成为一名程序设计专家,这与其他学科一样:你不能期望花四个月、半年或一年时间就成为一名生物学专家、一名数学家、一名自然语言(如中文、英文或丹麦文)方面的专家,或是一名小提琴演奏家。但如果你认真地学完了这本书,你可以期待也应该期待的是:你已经在程序设计领域有了一个很好的开始,已经可以写相对简单的、有用的程序,能读更复杂的程序,而且已经为进一步的学习打下了良好的理论和实践基础。
学习完这门入门课程后,进一步学习的最好方法是开发一个真正能被别人使用的程序。在完成这个项目之后或者同时(同时可能更好)学习一本专业水平的教材(如Stroustrup的《The C++ Programming Language》),学习一本与你做的项目相关的更专门的书(比如,你如果在做GUI相关项目的话,可选择关于Qt的书,如果在做分布式程序的话,可选择关于ACE的书),或者学习一本专注于C++某个特定方面的书(如Koenig和Moo的《Accel-erated C++》、Sutter的《Exceptional C++》或Gamma等人的《Design Patterns》)。完整的参考书目参见本引言或本书最后的参考文献。
最后,你应该学习另一门程序设计语言。我们认为,如果只懂一门语言,你是不可能成为软件领域的专家的(即使你并不是想做一名程序员)。
本书内容顺序的安排
讲授程序设计有很多方法。很明显,我们不赞同“我学习程序设计的方法就是最好的学习方法”这种流行的看法。为了方便学习,我们较早地提出一些仅仅几年前还是先进技术的内容。我们的设想是,本书内容的顺序完全由你学习程序设计过程中遇到的问题来决定,随着你对程序设计的理解和实际动手能力的提高,一个主题一个主题地平滑向前推进。本书的叙述顺序更像一部小说,而不是一部字典或者一种层次化的顺序。
一次性地学习所有程序设计原理、技术和语言功能是不可能的。因此,你需要选择其中一个子集作为起点。更一般地,一本教材或一门课程应该通过一系列的主题子集来引导学生。我们认为,选择适当的主题并给出重点是我们的责任。我们不能简单地罗列出所有内容,必须做出取舍;在每个学习阶段,我们选择省略的内容与选择保留的内容至少同样重要。
作为对照,这里列出我们决定不采用的教学方法(仅仅是一个缩略列表),对你可能有用:
C优先:用这种方法学习C++完全是浪费学生的时间,学生能用来求解问题的语言功能、技术和库比所需的要少得多,这样的程序设计实践很糟糕。与C相比,C++能提供更强的类型检查、对新手来说更好的标准库以及用于错误处理的异常机制。
自底向上:学生本该学习好的、有效的程序设计技巧,但这种方法分散了学生的注意力。学生在求解问题过程中所能依靠的编程语言和库方面的支持明显不足,这样的编程实践质量很低、毫无用处。
如果你介绍某些内容,就必须介绍它的全部:这实际上意味着自底向上方法(一头扎进涉及的每个主题,越陷越深)。这种方法硬塞给初学者很多他们并不感兴趣而且可能很长时间内都用不上的技术细节,令他们厌烦。这样做毫无必要,因为一旦学会了编程,你完全可以自己到手册中查找技术细节。这是手册擅长的方面,如果用来学习基本概念就太可怕了。
自顶向下:这种方法对一个主题从基本原理到细节逐步介绍,倾向于把读者的注意力从程序设计的实践层面上转移开,迫使读者一直专注于上层概念,而没有任何机会实际体会这些概念的重要性。这是错误的,例如,如果你没有实际体会到编写程序是那么容易出错,而修正一个错误是那么困难,你就无法体会到正确的软件开发原理。
抽象优先:这种方法专注于一般原理,保护学生不受讨厌的现实问题限制条件的困扰,这会导致学生轻视实际问题、语言、工具和硬件限制。通常,这种方法基于“教学用语言”——一种将来不可能实际应用,有意将学生与实际的硬件和系统问题隔绝开的语言。
软件工程理论优先:这种方法和抽象优先的方法具有与自顶向下方法一样的缺点:没有具体实例和实践体验,你无法体会到抽象理论的价值和正确的软件开发实践技巧。
面向对象先行:面向对象程序设计是一种组织代码和开发工作的很好方法,但并不是唯一有效的方法。特别是,以我们的体会,在类型系统和算法式编程方面打下良好的基础,是学习类和类层次设计的前提条件。本书确实在一开始就使用了用户自定义类型(一些人称之为“对象”),但我们直到第6章才展示如何设计一个类,而直到第17章才展示了类层次。
相信魔法:这种方法只是向初学者展示强有力的工具和技术,而不介绍其下蕴含的技术和特性。这让学生只能去猜这些工具和技术为什么会有这样的表现,使用它们会付出多大代价,以及它们恰当的应用范围,而通常学生会猜错!这会导致学生过分刻板地遵循相似的工作模式,成为进一步学习的障碍。
自然,我们不会断言这些我们没有采用的方法毫无用处。实际上,在介绍一些特定的内容时,我们使用了其中一些方法,学生能体会到这些方法在这些特殊情况下的优点。但是,当学习程序设计是以实用为目标时,我们不把这些方法作为一般的教学方法,而是采用其他方法:主要是具体优先和深度优先方法,并对重点概念和技术加以强调。
程序设计和程序设计语言
我们首先介绍程序设计,把程序设计语言放在第二位。我们介绍的程序设计方法适用于任何通用的程序设计语言。我们的首要目的是帮助你学习一般概念、理论和技术,但是这些内容不能孤立地学习。例如,不同程序设计语言在语法细节、编程思想的表达以及工具等方面各不相同。但对于编写无错代码的很多基本技术,如编写逻辑简单的代码(第5章和第6章),构造不变式(9.4.3节),以及接口和实现细节分离(9.7节和19.1~19.2节)等,不同程序设计语言则差别很小。
程序设计技术的学习必须借助于一门程序设计语言,代码设计、组织和调试等技巧是不可能从抽象理论中学到的。你必须用某种程序设计语言编写代码,从中获取实践经验。这意味着你必须学习一门程序设计语言的基本知识。这里说“基本知识”,是因为花几个星期就能掌握一门主流实用编程语言全部内容的日子已经一去不复返了。本书中C++语言相关的内容只是我们选出的它的一个子集,是与编写高质量代码关系最紧密的那部分内容。而且,我们所介绍的C++特性都是你肯定会用到的,因为这些特性要么是出于逻辑完整性的要求,要么是C++社区中最常见的。
可移植性
编写运行于多种平台的C++程序是很常见的情况。一些重要的C++应用甚至运行于我们闻所未闻的平台!我们认为可移植性和对多种平台架构/操作系统的利用是非常重要的特性。本质上,本书的每个例子都不仅是ISO标准C++程序,还是可移植的。除非特别指出,本书的代码都能运行于任何一种C++实现,并且确实已经在多种计算机平台和操作系统上测试通过了。
不同系统编译、链接和运行C++程序的细节各不相同,如果每当提及一个实现问题时就介绍所有系统和所有编译器的细节,是非常单调乏味的。我们在附录B中给出了Windows平台Visual Studio和Microsoft C++入门的大部分基本知识。
如果你在使用任何一种流行的但相对复杂的IDE(集成开发环境,Integrated Development Environment)时遇到了困难,我们建议你尝试命令行工作方式,它极其简单。例如,下面给出的是在Unix或Linux平台用GNU C++编译器编译、链接和运行一个包含两个源文件my_f?ile1.cpp和my_f?ile2.cpp的简单程序所需的全部命令:
是的,这真的就是全部。
提示标记
为了方便读者回顾本书,以及帮读者发现第一次阅读时遗漏的关键内容,我们在页边空白处放置三种“提示标记”:
:概念和技术。
:建议。
:警告。
附言
很多章最后都提供了一个简短的“附言”,试图给出本章所介绍内容的全景描述。我们这样做是因为意识到,知识可能是(而且通常就是)令人畏缩的,只有当完成了习题、学习了进一步的章节(应用了本章中提出的思想)并进行了复习之后才能完全理解。不要恐慌,放轻松,这是很自然的,可以预料到的。你不可能一天之内就成为专家,但可以通过学习本书逐步成为一名合格的程序员。学习过程中,你会遇到很多知识、实例和技术,很多程序员已经从中发现了令人激动的和有趣的东西。
程序设计和计算机科学
程序设计就是计算机科学的全部吗?答案当然是否定的!我们提出这一问题的唯一原因就是确实曾有人将其混淆。本书会简单涉及计算机科学的一些主题,如算法和数据结构,但我们的目标还是讲授程序设计:设计和实现程序。这比广泛接受的计算机科学的概念更宽,但也更窄:
更宽,因为程序包含很多专业技巧,通常不能归类于任何一种科学。
更窄,因为就涉及的计算机科学的内容而言,我们没有系统地给出其基础。
本书的目标是作为一门计算机科学课程的一部分(如果成为一个计算机科学家是你的目标的话),成为软件构造和维护领域第一门课程的基础(如果你希望成为一个程序员或者软件工程师的话),总之是更大的完整系统的一部分。
本书自始至终都依赖计算机科学,我们也强调基本原理,但我们是以理论和经验为基础来讲授程序设计,是把它作为一种实践技能,而不是一门科学。
创造性和问题求解
本书的首要目标是帮助你学会用代码表达自己的思想,而不是教你如何获得这些思想。沿着这样一个思路,我们给出很多实例,展示如何求解问题。每个实例通常先分析问题,随后对求解方案逐步求精。我们认为程序设计本身是问题求解的一种描述形式:只有完全理解了一个问题及其求解方案,你才能用程序来正确表达它;而只有通过构造和测试一个程序,你才能确定你对问题和求解方案的理解是完整、正确的。因此,程序设计本质上是理解问题和求解方案工作的一部分。但是,我们的目标是通过实例而不是通过“布道”或是问题求解详细“处方”的展示来说明这一切。
反馈方法
我们不认为存在完美的教材;个人的需求总是差别很大的。但是,我们愿意尽力使本书和支持材料更接近完美。为此,我们需要大家的反馈,脱离读者是不可能写出好教材的。请大家给我们发送反馈报告,包括内容错误、排版错误、含混的文字、缺失的解释等。我们也感谢有关更好的习题、更好的实例、增加内容、删除内容等的建议。大家提出的建设性意见会帮助将来的读者,我们会将勘误表张贴在支持网站:www.stroustrup.com/Programming。
参考文献
下面列出了前面提及的参考文献,以及可能对你有用的一些文献。
Becker, Pete, ed. The C++ Standard. ISO/IEC 14882:2011.
Blanchette, Jasmin, and Mark Summerf?ield. C++ GUI Programming with Qt 4, Second Edition. Prentice Hall, 2008. ISBN 0132354160.
Koenig, Andrew, and Barbara E. Moo. Accelerated C++: Practical Programming by Example. Addison-Wesley, 2000. ISBN 020170353X.
Meyers, Scott. Effective C++: 55 Specif?ic Ways to Improve Your Programs and Designs, Third Edition. Addison-Wesley, 2005. ISBN 0321334876.
Schmidt, Douglas C., and Stephen D. Huston. C++ Network Programming, Volume 1: Mastering Complexity with ACE and Patterns. Addison-Wesley, 2001. ISBN 0201604647.
Schmidt, Douglas C., and Stephen D. Huston. C++ Network Programming, Volume 2: Systematic Reuse with ACE and Frameworks. Addison-Wesley, 2002. ISBN 0201795256.
Stroustrup, Bjarne. The Design and Evolution of C++. Addison-Wesley, 1994. ISBN 0201543303.
Stroustrup, Bjarne. “Learning Standard C++ as a New Language.” C/C++ Users Journal, May 1999.
Stroustrup, Bjarne. The C++ Programming Language, Fourth Edition. Addison-Wesley, 2013. ISBN 0321563840.
Stroustrup, Bjarne. A Tour of C++. Addison-Wesley, 2013. ISBN 0321958314.
Sutter, Herb. Exceptional C++: 47 Engineering Puzzles, Programming Problems, and Solutions. Addison-Wesley, 1999. ISBN 0201615622.
更全面的参考文献列表可以在本书最后找到。
---------------------------C++程序设计:原理与实践(进阶篇)(原书第2版)---------------------------
当实际地形与地图不符时,相信实际地形。
——瑞士军队谚语
讲授和学习本书的方法
我们是如何帮助你学习的?又是如何安排学习进程的?我们的做法是,尽力为你提供编写高效的实用程序所需的最基本的概念、技术和工具,包括程序组织、调试和测试、类设计、计算、函数和算法设计、绘图方法(仅介绍二维图形)、图形用户界面(GUI)、文本处理、正则表达式匹配、文件和流输入输出(I/O)、内存管理、科学/数值/工程计算、设计和编程思想、C++标准库、软件开发策略、C语言程序设计技术。认真完成这些内容的学习,我们会学到如下程序设计技术:过程式程序设计(C语言程序设计风格)、数据抽象、面向对象程序设计和泛型程序设计。本书的主题是程序设计,也就是表达代码意图所需的思想、技术和工具。C++语言是我们的主要工具,因此我们比较详细地描述了很多C++语言的特性。但请记住,C++只是一种工具,而不是本书的主题。本书是“用C++语言进行程序设计”,而不是“C++和一点程序设计理论”。
我们介绍的每个主题都至少出于两个目的:提出一种技术、概念或原理,介绍一个实用的语言特性或库特性。例如,我们用一个二维图形绘制系统的接口展示如何使用类和继承。这使我们节省了篇幅(也节省了你的时间),并且还强调了程序设计不只是简单地将代码拼装起来以尽快地得到一个结果。C++标准库是这种“双重作用”例子的主要来源,其中很多主题甚至具有三重作用。例如,我们会介绍标准库中的向量类vector,用它来展示一些广泛使用的设计技术,并展示很多用来实现vector的程序设计技术。我们的一个目标是向你展示一些主要的标准库功能是如何实现的,以及它们如何与硬件相配合。我们坚持认为一个工匠必须了解他的工具,而不是仅仅把工具当作“有魔力的东西”。
对于一个程序员来说,总是会对某些主题比对其他主题更感兴趣。但是,我们建议你不要预先判断你需要什么(你怎么知道你将来会需要什么呢?),至少每一章都要浏览一下。如果你学习本书是作为一门课程的一部分,你的老师会指导你如何选择学习内容。
我们的教学方法可以描述为“深度优先”,同时也是“具体优先”和“基于概念”。首先,我们快速地(好吧,是相对快速地,从第1章到第11章)将一些编写小的实用程序所需的技巧提供给你。在这期间,我们还简明扼要地提出很多工具和技术。我们着重于简单具体的代码实例,因为相对于抽象概念,人们能更快领会具体实例,这就是多数人的学习方法。在最初阶段,你不应期望理解每个小的细节。特别是,你会发现对刚刚还工作得好好的程序稍加改动,便会呈现出“神秘”的效果。尽管如此,你还是要尝试一下!还有,请完成我们提供的简单练习和习题。请记住,在学习初期你只是没有掌握足够的概念和技巧来准确判断什么是简单的,什么是复杂的。请等待一些惊奇的事情发生,并从中学习吧。
我们会快速通过这样一个初始阶段——我们想尽可能快地带你进入编写有趣程序的阶段。有些人可能会质疑,“我们的进展应该慢些、谨慎些,我们应该先学会走,再学跑!”但是你见过小孩学习走路吗?实际上小孩在学会平稳地慢慢走路之前就开始尝试跑了。与之相似,你可以先勇猛向前,偶尔摔一跤,从中获得编程的感觉,然后再慢下来,获得必要的精确控制能力和准确的理解。你必须在学会走之前就开始跑!
你不要投入大量精力试图学习一些语言或技术细节的所有相关内容。例如,你可以熟记所有C++的内置类型及其使用规则。你当然可以这么做,而且这么做会使你觉得自己很博学。但是,这不会使你成为一名程序员。如果你学习中略过一些细节,将来可能偶尔会因为缺少相关知识而被“灼伤”,但这是获取编写好程序所需的完整知识结构的最快途径。注意,我们的这种方法本质上就是小孩学习其母语的方法,也是教授外语的最有效方法。有时你不可避免地被难题困住,我们鼓励你向授课老师、朋友、同事、指导教师等寻求帮助。请放心,在前面这些章节中,所有内容本质上都不困难。但是,很多内容是你所不熟悉的,因此最初可能会感觉有点难。
随后,我们介绍一些入门技巧来拓宽你的知识。我们通过实例和习题来强化你的理解,为你提供一个程序设计的概念基础。
我们非常强调思想和原理。思想能指导你求解实际问题——可以帮助你知道在什么情况下问题求解方案是好的、合理的。你还应该理解这些思想背后的原理,从而理解为什么要接受这些思想,为什么遵循这些思想会对你和使用你的代码的用户有帮助。没有人会满意“因为事情就是如此”这样的解释。更为重要的是,如果真正理解了思想和原理,你就能将自己已知的知识推广到新的情况;就能用新的方法将思想和工具结合来解决新的问题。知其所以然是学会程序设计技巧所必需的。相反,仅仅不求甚解地记住大量规则和语言特性有很大局限,是错误之源,是在浪费时间。我们认为你的时间很珍贵,尽量不要浪费它。
我们把很多C++语言层面的技术细节放在了附录和手册中,你可以随时按需查找。我们假定你有能力查找到需要的信息,你可以借助目录来查找信息。不要忘了编译器和互联网的在线功能。但要记住,要对所有互联网资源保持足够的怀疑,直至你有足够的理由相信它们。因为很多看起来很权威的网站实际上是由程序设计新手或者想要出售什么东西的人建立的。而另外一些网站,其内容都是过时的。我们在支持网站www.stroustrup.com/Programming上列出了一些有用的网站链接和信息。
请不要过于急切地期盼“实际的”例子。我们理想的实例都是能直接说明一种语言特性、一个概念或者一种技术的简短代码。很多现实世界中的实例比我们给出的实例要凌乱很多,而且所能展示的知识也不比我们的实例更多。包含数十万行代码的成功商业程序中所采用的技术,我们用几个50行规模的程序就能展示出来。理解现实世界程序的最快途径是好好研究一些基础的小程序。
另一方面,我们不会用“聪明可爱的风格”来阐述我们的观点。我们假定你的目标是编写供他人使用的实用程序,因此书中给出的实例要么是用来说明语言特性,要么是从实际应用中提取出来的。我们的叙述风格都是用专业人员对(将来的)专业人员的那种口气。
一般方法
本书的内容组织适合从头到尾一章一章地阅读,当然,你也常常要回过头来对某些内容读上第二遍、第三遍。实际上,这是一种明智的方法,因为当遇到还看不出什么门道的地方时,你通常会快速掠过。对于这种情况,你最终还是会再次回到这个地方。然而,这么做要适度,因为除了交叉引用之外,对本书其他部分,你随便翻开一页,就从那里开始学习,并希望成功,是不可能的。本书每一节、每一章的内容安排,都假定你已经理解了之前的内容。
本书的每一章都是一个合理的自包含单元,这意味着应一口气读完(当然这只是理论上,实际上由于学生紧密的学习计划,不总是可行)。这是将内容划分为章的主要标准。其他标准包括:从简单练习和习题的角度,每章是一个合适的单元;每一章提出一些特定的概念、思想或技术。这种标准的多样性使得少数章过长,所以不要教条地遵循“一口气读完”的准则。特别是当你已经考虑了思考题,做了简单练习,并做了一些习题时,你通常会发现你需要回过头去重读一些小节和几天前读过的内容。
“它回答了我想到的所有问题”是对一本教材常见的称赞,这对细节技术问题是很理想的,而早期的读者也发现本书有这样的特性。但是,这不是全部的理想,我们希望提出初学者可能想不到的问题。我们的目标是,回答那些你在编写供他人使用的高质量软件时需要考虑的问题。学习回答好的(通常也是困难的)问题是学习如何像一个程序员那样思考所必需的。只回答那些简单的、浅显的问题会使你感觉良好,但无助于你成长为一名程序员。
我们努力尊重你的智力,珍惜你的时间。在本书中,我们以专业性而不是精明伶俐为目标,宁可有节制地表达一个观点而不大肆渲染它。我们尽力不夸大一种程序设计技术或一个语言特性的重要性,但请不要因此低估“这通常是有用的”这种简单陈述的重要程度。如果我们平静地强调某些内容是重要的,意思是你如果不掌握它,或早或晚都会因此而浪费时间。
我们不会伪称本书中的思想和工具是完美的。实际上没有任何一种工具、库、语言或者技术能够解决程序员所面临的所有难题,至多能帮助你开发、表达你的问题求解方案而已。我们尽量避免“无害的谎言”,也就是说,我们会尽力避免过于简单的解释,虽然这些解释清晰且易理解,但在实际编程和问题求解时却容易弄错。另一方面,本书不是一本参考手册,如果需要C++详细完整的描述,请参考Bjarne Stroustrup的《The C++ Programming Language》第4版(Addison-Wesley出版社,2013年)和ISO的C++标准。
简单练习和习题等
程序设计不仅仅是一种脑力活动,实际动手编写程序是掌握程序设计技巧必不可少的一环。本书提供两个层次的程序设计练习:
简单练习:简单练习是一种非常简单的习题,其目的是帮助学生掌握一些相对死板的实际编程技巧。一个简单练习通常由一系列的单个程序修改练习组成。你应该完成所有简单练习。完成简单练习不需要很强的理解能力、很聪明或者很有创造性。简单练习是本书的基本组成部分,如果你没有完成简单练习,就不能说完成了本书的学习。
习题:有些习题比较简单,有些则很难,但多数习题都是想给学生留下一定的创造和想象空间。如果时间紧张,你可以做少量习题,但题量至少应该能使你弄清楚哪些内容对你来说比较困难,在此基础上应该再多做一些,这是你的成功之道。我们希望本书的习题都是学生能够做出来的,而不是需要超乎常人的智力才能解答的复杂难题。但是,我们还是期望本书习题能给你足够多的挑战,能用光甚至是最好的学生的所有时间。我们不期待你能完成所有习题,但请尽情尝试。
另外,我们建议每个学生都能参与到一个小的项目中去(如果时间允许,能参与更多项目当然就更好了)。一个项目的目的就是要编写一个完整的有用程序。理想情况下,一个项目由一个多人小组(比如三个人)共同完成。大多数人会发现做项目非常有趣,并在这个过程中学会如何把很多事情组织在一起。
一些人喜欢在读完一章之前就把书扔到一边,开始尝试做一些实例程序;另一些人则喜欢把一章读完后,再开始编码。为了帮助前一种读者,我们用“试一试”板块给出了对于编程实践的一些简单建议。一个“试一试”通常来说就是一个简单练习,而且只着眼于前面刚刚介绍的主题。如果你略过了一个“试一试”而没有去尝试它,那么最好在做这一章的简单练习时做一下这个题目。“试一试”要么是该章简单练习的补充,要么干脆就是其中的一部分。
在每章末尾你都会看到一些思考题,我们设置这些思考题是想为你指出这一章中的重点内容。一种学习思考题的方法是把它们作为习题的补充:习题关注程序设计的实践层面,而思考题则试图帮你强化思想和概念。因此,思考题有点像面试题。
每章最后都有“术语”一节,给出本章中提出的程序设计或C++方面的基本词汇表。如果你希望理解别人关于程序设计的陈述,或者想明确表达出自己的思想,就应该首先弄清术语表中每个术语的含义。
重复是学习的有效手段,我们希望每个重要的知识点都在书中至少出现两次,并通过习题再次强调。
进阶学习
当你完成本书的学习时,是否能成为一名程序设计和C++方面的专家呢?答案当然是否定的!如果做得好的话,程序设计会是一门建立在多种专业技能上的精妙的、深刻的、需要高度技巧的艺术。你不能期望花四个月时间就成为一名程序设计专家,这与其他学科一样:你不能期望花四个月、半年或一年时间就成为一名生物学专家、一名数学家、一名自然语言(如中文、英文或丹麦文)方面的专家,或是一名小提琴演奏家。但如果你认真地学完了这本书,你可以期待也应该期待的是:你已经在程序设计领域有了一个很好的开始,已经可以写相对简单的、有用的程序,能读更复杂的程序,而且已经为进一步的学习打下了良好的理论和实践基础。
学习完这门入门课程后,进一步学习的最好方法是开发一个真正能被别人使用的程序。在完成这个项目之后或者同时(同时可能更好)学习一本专业水平的教材(如Stroustrup的《The C++ Programming Language》),学习一本与你做的项目相关的更专门的书(比如,你如果在做GUI相关项目的话,可选择关于Qt的书,如果在做分布式程序的话,可选择关于ACE的书),或者学习一本专注于C++某个特定方面的书(如Koenig和Moo的《Accel-erated C++》、Sutter的《Exceptional C++》或Gamma等人的《Design Patterns》)。完整的参考书目参见本引言或本书最后的参考文献。
最后,你应该学习另一门程序设计语言。我们认为,如果只懂一门语言,你是不可能成为软件领域的专家的(即使你并不是想做一名程序员)。
本书内容顺序的安排
讲授程序设计有很多方法。很明显,我们不赞同“我学习程序设计的方法就是最好的学习方法”这种流行的看法。为了方便学习,我们较早地提出一些仅仅几年前还是先进技术的内容。我们的设想是,本书内容的顺序完全由你学习程序设计过程中遇到的问题来决定,随着你对程序设计的理解和实际动手能力的提高,一个主题一个主题地平滑向前推进。本书的叙述顺序更像一部小说,而不是一部字典或者一种层次化的顺序。
一次性地学习所有程序设计原理、技术和语言功能是不可能的。因此,你需要选择其中一个子集作为起点。更一般地,一本教材或一门课程应该通过一系列的主题子集来引导学生。我们认为,选择适当的主题并给出重点是我们的责任。我们不能简单地罗列出所有内容,必须做出取舍;在每个学习阶段,我们选择省略的内容与选择保留的内容至少同样重要。
作为对照,这里列出我们决定不采用的教学方法(仅仅是一个缩略列表),对你可能有用:
C优先:用这种方法学习C++完全是浪费学生的时间,学生能用来求解问题的语言功能、技术和库比所需的要少得多,这样的程序设计实践很糟糕。与C相比,C++能提供更强的类型检查、对新手来说更好的标准库以及用于错误处理的异常机制。
自底向上:学生本该学习好的、有效的程序设计技巧,但这种方法分散了学生的注意力。学生在求解问题过程中所能依靠的编程语言和库方面的支持明显不足,这样的编程实践质量很低、毫无用处。
如果你介绍某些内容,就必须介绍它的全部:这实际上意味着自底向上方法(一头扎进涉及的每个主题,越陷越深)。这种方法硬塞给初学者很多他们并不感兴趣而且可能很长时间内都用不上的技术细节,令他们厌烦。这样做毫无必要,因为一旦学会了编程,你完全可以自己到手册中查找技术细节。这是手册擅长的方面,如果用来学习基本概念就太可怕了。
自顶向下:这种方法对一个主题从基本原理到细节逐步介绍,倾向于把读者的注意力从程序设计的实践层面上转移开,迫使读者一直专注于上层概念,而没有任何机会实际体会这些概念的重要性。这是错误的,例如,如果你没有实际体会到编写程序是那么容易出错,而修正一个错误是那么困难,你就无法体会到正确的软件开发原理。
抽象优先:这种方法专注于一般原理,保护学生不受讨厌的现实问题限制条件的困扰,这会导致学生轻视实际问题、语言、工具和硬件限制。通常,这种方法基于“教学用语言”——一种将来不可能实际应用,有意将学生与实际的硬件和系统问题隔绝开的语言。
软件工程理论优先:这种方法和抽象优先的方法具有与自顶向下方法一样的缺点:没有具体实例和实践体验,你无法体会到抽象理论的价值和正确的软件开发实践技巧。
面向对象先行:面向对象程序设计是一种组织代码和开发工作的很好方法,但并不是唯一有效的方法。特别是,以我们的体会,在类型系统和算法式编程方面打下良好的基础,是学习类和类层次设计的前提条件。本书确实在一开始就使用了用户自定义类型(一些人称之为“对象”),但我们直到第6章才展示如何设计一个类,而直到第17章才展示了类层次。
相信魔法:这种方法只是向初学者展示强有力的工具和技术,而不介绍其下蕴含的技术和特性。这让学生只能去猜这些工具和技术为什么会有这样的表现,使用它们会付出多大代价,以及它们恰当的应用范围,而通常学生会猜错!这会导致学生过分刻板地遵循相似的工作模式,成为进一步学习的障碍。
自然,我们不会断言这些我们没有采用的方法毫无用处。实际上,在介绍一些特定的内容时,我们使用了其中一些方法,学生能体会到这些方法在这些特殊情况下的优点。但是,当学习程序设计是以实用为目标时,我们不把这些方法作为一般的教学方法,而是采用其他方法:主要是具体优先和深度优先方法,并对重点概念和技术加以强调。
程序设计和程序设计语言
我们首先介绍程序设计,把程序设计语言放在第二位。我们介绍的程序设计方法适用于任何通用的程序设计语言。我们的首要目的是帮助你学习一般概念、理论和技术,但是这些内容不能孤立地学习。例如,不同程序设计语言在语法细节、编程思想的表达以及工具等方面各不相同。但对于编写无错代码的很多基本技术,如编写逻辑简单的代码(第5章和第6章),构造不变式(9.4.3节),以及接口和实现细节分离(9.7节和19.1~19.2节)等,不同程序设计语言则差别很小。
程序设计技术的学习必须借助于一门程序设计语言,代码设计、组织和调试等技巧是不可能从抽象理论中学到的。你必须用某种程序设计语言编写代码,从中获取实践经验。这意味着你必须学习一门程序设计语言的基本知识。这里说“基本知识”,是因为花几个星期就能掌握一门主流实用编程语言全部内容的日子已经一去不复返了。本书中C++语言相关的内容只是我们选出的它的一个子集,是与编写高质量代码关系最紧密的那部分内容。而且,我们所介绍的C++特性都是你肯定会用到的,因为这些特性要么是出于逻辑完整性的要求,要么是C++社区中最常见的。
可移植性
编写运行于多种平台的C++程序是很常见的情况。一些重要的C++应用甚至运行于我们闻所未闻的平台!我们认为可移植性和对多种平台架构/操作系统的利用是非常重要的特性。本质上,本书的每个例子都不仅是ISO标准C++程序,还是可移植的。除非特别指出,本书的代码都能运行于任何一种C++实现,并且确实已经在多种计算机平台和操作系统上测试通过了。
不同系统编译、链接和运行C++程序的细节各不相同,如果每当提及一个实现问题时就介绍所有系统和所有编译器的细节,是非常单调乏味的。我们在附录B中给出了Windows平台Visual Studio和Microsoft C++入门的大部分基本知识。
如果你在使用任何一种流行的但相对复杂的IDE(集成开发环境,Integrated Development Environment)时遇到了困难,我们建议你尝试命令行工作方式,它极其简单。例如,下面给出的是在Unix或Linux平台用GNU C++编译器编译、链接和运行一个包含两个源文件my_f?ile1.cpp和my_f?ile2.cpp的简单程序所需的全部命令:
是的,这真的就是全部。
提示标记
为了方便读者回顾本书,以及帮读者发现第一次阅读时遗漏的关键内容,我们在页边空白处放置三种“提示标记”:
:概念和技术。
:建议。
:警告。
附言
很多章最后都提供了一个简短的“附言”,试图给出本章所介绍内容的全景描述。我们这样做是因为意识到,知识可能是(而且通常就是)令人畏缩的,只有当完成了习题、学习了进一步的章节(应用了本章中提出的思想)并进行了复习之后才能完全理解。不要恐慌,放轻松,这是很自然的,可以预料到的。你不可能一天之内就成为专家,但可以通过学习本书逐步成为一名合格的程序员。学习过程中,你会遇到很多知识、实例和技术,很多程序员已经从中发现了令人激动的和有趣的东西。
程序设计和计算机科学
程序设计就是计算机科学的全部吗?答案当然是否定的!我们提出这一问题的唯一原因就是确实曾有人将其混淆。本书会简单涉及计算机科学的一些主题,如算法和数据结构,但我们的目标还是讲授程序设计:设计和实现程序。这比广泛接受的计算机科学的概念更宽,但也更窄:
更宽,因为程序包含很多专业技巧,通常不能归类于任何一种科学。
更窄,因为就涉及的计算机科学的内容而言,我们没有系统地给出其基础。
本书的目标是作为一门计算机科学课程的一部分(如果成为一个计算机科学家是你的目标的话),成为软件构造和维护领域第一门课程的基础(如果你希望成为一个程序员或者软件工程师的话),总之是更大的完整系统的一部分。
本书自始至终都依赖计算机科学,我们也强调基本原理,但我们是以理论和经验为基础来讲授程序设计,是把它作为一种实践技能,而不是一门科学。
创造性和问题求解
本书的首要目标是帮助你学会用代码表达自己的思想,而不是教你如何获得这些思想。沿着这样一个思路,我们给出很多实例,展示如何求解问题。每个实例通常先分析问题,随后对求解方案逐步求精。我们认为程序设计本身是问题求解的一种描述形式:只有完全理解了一个问题及其求解方案,你才能用程序来正确表达它;而只有通过构造和测试一个程序,你才能确定你对问题和求解方案的理解是完整、正确的。因此,程序设计本质上是理解问题和求解方案工作的一部分。但是,我们的目标是通过实例而不是通过“布道”或是问题求解详细“处方”的展示来说明这一切。
反馈方法
我们不认为存在完美的教材;个人的需求总是差别很大的。但是,我们愿意尽力使本书和支持材料更接近完美。为此,我们需要大家的反馈,脱离读者是不可能写出好教材的。请大家给我们发送反馈报告,包括内容错误、排版错误、含混的文字、缺失的解释等。我们也感谢有关更好的习题、更好的实例、增加内容、删除内容等的建议。大家提出的建设性意见会帮助将来的读者,我们会将勘误表张贴在支持网站:www.stroustrup.com/Programming。
参考文献
下面列出了前面提及的参考文献,以及可能对你有用的一些文献。
Becker, Pete, ed. The C++ Standard. ISO/IEC 14882:2011.
Blanchette, Jasmin, and Mark Summerf?ield. C++ GUI Programming with Qt 4, Second Edition. Prentice Hall, 2008. ISBN 0132354160.
Koenig, Andrew, and Barbara E. Moo. Accelerated C++: Practical Programming by Example. Addison-Wesley, 2000. ISBN 020170353X.
Meyers, Scott. Effective C++: 55 Specif?ic Ways to Improve Your Programs and Designs, Third Edition. Addison-Wesley, 2005. ISBN 0321334876.
Schmidt, Douglas C., and Stephen D. Huston. C++ Network Programming, Volume 1: Mastering Complexity with ACE and Patterns. Addison-Wesley, 2001. ISBN 0201604647.
Schmidt, Douglas C., and Stephen D. Huston. C++ Network Programming, Volume 2: Systematic Reuse with ACE and Frameworks. Addison-Wesley, 2002. ISBN 0201795256.
Stroustrup, Bjarne. The Design and Evolution of C++. Addison-Wesley, 1994. ISBN 0201543303.
Stroustrup, Bjarne. “Learning Standard C++ as a New Language.” C/C++ Users Journal, May 1999.
Stroustrup, Bjarne. The C++ Programming Language, Fourth Edition. Addison-Wesley, 2013. ISBN 0321563840.
Stroustrup, Bjarne. A Tour of C++. Addison-Wesley, 2013. ISBN 0321958314.
Sutter, Herb. Exceptional C++: 47 Engineering Puzzles, Programming Problems, and Solutions. Addison-Wesley, 1999. ISBN 0201615622.
更全面的参考文献列表可以在本书最后找到。