本节书摘来自华章出版社《 C++程序设计:原理与实践(进阶篇)》一书中作者[美] 本贾尼·斯特劳斯特鲁普(Bjarne Stroustrup) 著
刘晓光 李忠伟 王刚 译
更多章节内容可以访问云栖社区“华章计算机”公众号查看。
前 言
Programming: Principles and Practice Using C++, Second Edition
该死的鱼雷!全速前进。
——Admiral Farragut
程序设计是这样一门艺术,它将问题求解方案描述成计算机可以执行的形式。程序设计中很多工作都花费在寻找求解方案以及对其求精上。通常,只有在真正编写程序求解一个问题的过程中才会对问题本身理解透彻。
本书适合于那些从未有过编程经验但愿意努力学习程序设计技术的初学者,它能帮助读者理解使用C++语言进行程序设计的基本原理并获得实践技巧。本书的目标是使你获得足够多的知识和经验,以便能使用最新、最好的技术进行简单有用的编程工作。达到这一目标需要多长时间呢?作为大学一年级课程的一部分,你可以在一个学期内完成这本书的学习(假定你有另外四门中等难度的课程)。如果你是自学的话,不要期望能花费更少的时间完成学习(一般来说,每周15个小时,14周是合适的学时安排)。
三个月可能看起来是一段很长的时间,但要学习的内容很多。写第一个简单程序之前,就要花费大约一个小时。而且,所有学习过程都是渐进的:每一章都会介绍一些新的有用的概念,并通过真实应用中的例子来阐述这些概念。随着学习进程的推进,你通过程序代码表达思想的能力——让计算机按你的期望工作的能力,会逐渐稳步地提高。我绝不会说:“先学习一个月的理论知识,然后看看你是否能使用这些理论吧。”
为什么要学习程序设计呢?因为我们的文明是建立在软件之上的。如果不理解软件,那么你将退化到只能相信“魔术”的境地,并且将被排除在很多最为有趣、最具经济效益和社会效益的领域之外。当我谈论程序设计时,我所想到的是整个计算机程序家族,从带有GUI(图形用户界面)的个人计算机程序,到工程计算和嵌入式系统控制程序(如数码相机、汽车和手机中的程序),以及文字处理程序等,在很多日常应用和商业应用中都能看到这些程序。程序设计与数学有些相似,认真去做的话,会是一种非常有用的智力训练,可以提高我们的思考能力。然而,由于计算机能做出反馈,程序设计不像大多数数学形式那么抽象,因而对多数人来说更易接受。可以说,程序设计是一条能够打开你的眼界,将世界变得更美好的途径。最后,程序设计可以是非常有趣的。
为什么学习C++这门程序设计语言呢?学习程序设计是不可能不借助一门程序设计语言的,而C++直接支持现实世界中的软件所使用的那些关键概念和技术。C++是使用最为广泛的程序设计语言之一,其应用领域几乎没有局限。从大洋深处到火星表面,到处都能发现C++程序的身影。C++是由一个开放的国际标准组织全面考量、精心设计的。在任何一种计算机平台上都能找到高质量的、免费的C++实现。而且,用C++所学到的程序设计思想,大多数可直接用于其他程序设计语言,如C、C#、Fortran以及Java。最后一个原因,我喜欢C++适合编写优美、高效的代码这一特点。
本书不是初学程序设计的最简单入门教材,我写此书的用意也不在此。我为本书设定的目标是——这是一本能让你学到基本的实用编程技术的最简单书籍。这是一个非常雄心勃勃的目标,因为很多现代软件所依赖的技术,不过才出现短短几年时间而已。
我的基本假设是:你希望编写供他人使用的程序,并愿意认真负责地以较高质量完成这个工作,也就是说,假定你希望达到专业水准。因此,我为本书选择的主题覆盖了开始学习实用编程技术所需要的内容,而不只是那些容易讲授和容易学习的内容。如果某种技术是你做好基本编程工作所需要的,那么本书就会介绍它,同时展示用以支持这种技术的编程思想和语言工具,并提供相应的练习,期望你通过做这些练习来熟悉这种技术。但如果你只想了解“玩具程序”,那么你能学到的将远比我所提供的少得多。另一方面,我不会用一些实用性很低的内容来浪费你的时间,本书介绍的内容都是你在实践中几乎肯定会用到的。
如果你只是希望直接使用别人编写的程序,而不想了解其内部原理,也不想亲自向代码中加入重要的内容,那么本书不适合你,采用另一本书或另一种程序设计语言会更好些。如果这大概就是你对程序设计的看法,那么请同时考虑一下你从何得来的这种观点,它真的满足你的需求吗?人们常常低估程序设计的复杂程度和它的重要性。我不愿看到,你不喜欢程序设计是因为你的需求与我所描述的软件世界之间不匹配而造成的。信息技术世界中有很多地方是不要求程序设计知识的。本书面向的是那些确实希望编写和理解复杂计算机程序的人。
考虑到本书的结构和注重实践的特点,它也可以作为学习程序设计的第二本书,适合那些已经了解一点C++的人,以及那些会用其他语言编程而现在想学习C++的人。如果你属于其中一类,我不好估计你学习这本书要花费多长时间。但我可以给你的建议是,多做练习。因为你在学习中常见的一个问题是习惯用熟悉的、旧的方式编写程序,而不是在适当的地方采用新技术,多做练习会帮助你克服这个问题。如果你曾经按某种更为传统的方式学习过C++,那么在进行到第7章之前,你会发现一些令你惊奇的、有用的内容。除非你的名字是Stroustrup,否则你会发现我在本书中所讨论的内容不是“你父辈的C++”。
学习程序设计要靠编程实践。在这一点上,程序设计与其他需要实践学习的技艺是相似的。你不可能仅仅通过读书就学会游泳、演奏乐器或者开车,必须进行实践。同样,你也不可能不读写大量代码就学会程序设计。本书给出了大量代码实例,都配有说明文字和图表。你需要通过读这些代码来理解程序设计的思想、概念和原理,并掌握用来表达这些思想、概念和原理的程序设计语言的特性。但有一点很重要,仅仅读代码是学不会编程实践技巧的。为此,你必须进行编程练习,通过编程工具熟悉编写、编译和运行程序。你需要亲身体验编程中会出现的错误,学习如何修改它们。总之,在学习程序设计的过程中,编写代码的练习是不可替代的。而且,这也是乐趣所在!
另一方面,程序设计远非遵循一些语法规则和阅读手册那么简单。本书的重点不在于C++的语法,而在于理解基础思想、原理和技术,这是一名好程序员所必备的。只有设计良好的代码才有机会成为一个正确、可靠和易维护的系统的一部分。而且,“基础”意味着延续性:当现在的程序设计语言和工具演变甚至被取代后,这些基础知识仍会保持其重要性。
那么计算机科学、软件工程、信息技术等又如何呢?它们都属于程序设计范畴吗?当然不是!但程序设计是一门基础性的学科,是所有计算机相关领域的基础,在计算机科学领域占有重要的地位。本书对算法、数据结构、用户接口、数据处理和软件工程等领域的重要概念和技术进行了简要介绍,但本书不能替代对这些领域的全面、均衡的学习。
代码可以很有用,同样可以很优美。本书会帮你了解这一点,同时理解优美的代码意味着什么,并帮你掌握构造优美代码的原理和实践技巧。祝你学习程序设计顺利!
致学生
到目前为止,我在德州农工大学已经用本书教过几千名大一新生,其中60%曾经有过编程经历,而剩余40%从未见过哪怕一行代码。大多数学生的学习是成功的,所以你也可以成功。
你不一定是在某门课程中学习本书,本书也广泛用于自学。然而,不管你学习本书是作为课程的一部分还是自学,都要尽量与他人协作。程序设计有一个不好的名声——它是一种个人活动,这是不公正的。大多数人在作为一个有共同目标的团体的一份子时,工作效果更好,学习得更快。与朋友一起学习和讨论问题不是“作弊”,而是取得进步最有效同时也是最快乐的途径。如果没有特殊情况的话,与朋友一起工作会促使你表达出自己的思想,这正是测试你对问题理解和确认你的记忆的最有效方法。你没有必要独自解决所有编程语言和编程环境上的难题。但是,请不要自欺欺人——不去完成那些简单练习和大量的习题(即使没有老师督促你,你也不应这样做)。记住:程序设计(尤其)是一种实践技能,需要通过实践来掌握。如果你不编写代码(完成每章的若干习题),那么阅读本书就纯粹是一种无意义的理论学习。
大多数学生,特别是那些爱思考的好学生,有时会对自己努力工作是否值得产生疑问。当你产生这样的疑问时,休息一会儿,重新读一下前言,读一下第1章和第22章。在那里,我试图阐述我在程序设计中发现了哪些令人兴奋的东西,以及为什么我认为程序设计是能为世界带来积极贡献的重要工具。如果你对我的教学哲学和一般方法有疑问,请阅读引言。
你可能会对本书的厚度感到担心。本书如此之厚的一部分原因是,我宁愿反复重复一些解释说明或增加一些实例,而不是让你自己到处找这些内容,这应该令你安心。另外一个主要原因是,本书的后半部分是一些参考资料和补充资料,供你想要深入了解程序设计的某个特定领域(如嵌入式系统程序设计、文本分析或数值计算)时查阅。
还有,学习中请耐心些。学习任何一种重要的、有价值的新技能都要花费一些时间,而这是值得的。
致教师
本书不是传统的计算机科学导论书籍,而是一本关于如何构造能实际工作的软件的书。因此本书省略了很多计算机科学系学生按惯例要学习的内容(图灵完全、状态机、离散数学、乔姆斯基文法等)。硬件相关的内容也省略了,因为我假定学生从幼儿园开始就已经通过不同途径使用过计算机了。本书也不准备涉及一些计算机科学领域最重要的主题。本书是关于程序设计的(或者更一般地说,是关于如何开发软件的),因此关注的是少量主题的更深入的细节,而不是像传统计算机课程那样讨论很多主题。本书只试图做好一件事,而且计算机科学也不是一门课程可以囊括的。如果本书被计算机科学、计算机工程、电子工程(我们最早的很多学生都是电子工程专业的)、信息科学或者其他相关专业所采用,我希望这门课程能和其他一些课程一起进行,共同形成对计算机科学的完整介绍。
请阅读引言,那里有对我的教学哲学、一般方法等的介绍。请在教学过程中尝试将这些观点传达给你的学生。
ISO标准C++
C++由一个ISO标准定义。第一个ISO C++标准于1998年获得批准,所以那个版本的C++被称为C++98。写本书第1版时,我正从事C++11的设计工作。最令人沮丧的是,当时我还不能使用一些新语言特性(如统一初始化、范围for循环、move语义、lambda表达式、concept等)来简化原理和技术的展示。不过,由于设计该书时考虑到了C++11,所以很容易在合适的地方添加这些特性。在写作本版时,C++标准是2011年批准的C++11,2014 ISO标准C++14中的一些特性正在进入主流的C++实现中。本书中使用的语言标准是C++11,并涉及少量的C++14特性。例如,如果你的编译器不能编译下面的代码:
可用如下代码替代:
若你的编译器不支持C++11,请换一个新的编译器。好的、现代的C++编译器可从多处下载,见www.stroustrup.com/compilers.html。使用较早且缺少支持的语言版本会引入不必要的困难。
资源
本书支持网站的网址为www.stroustrup.com/Programming,其中包含了各种使用本书讲授和学习程序设计所需的辅助资料。这些资料可能会随着时间的推移不断改进,但对于初学者,现在可以找到这些资料:
基于本书的讲义幻灯片;
一本教师指南;
本书中使用的库的头文件和实现;
本书中实例的代码;
某些习题的解答;
可能会有用处的一些链接;
勘误表。
欢迎随时提出对这些资料的改进意见。
致谢
我要特别感谢已故的同事和联合导师Lawrence“Pete”Petersen,很久以前,在我还未感受到教授初学者的惬意时,是他鼓励我承担这项工作,并向我传授了很多能令课程成功的教学经验。没有他,这门课程的首次尝试就会失败。他参与了这门课程最初的建设,本书就是为这门课程所著。他还和我一起反复讲授这门课程,汲取经验,不断改进课程和本书。在本书中我使用的“我们”这个字眼,最初的意思就是指“我和Pete”。
我要感谢那些直接或间接帮助过我撰写本书的学生、助教和德州农工大学讲授ENGR 112、113及CSCE 121课程的教师,以及Walter Daugherity、Hyunyoung Lee、Teresa Leyk、Ronnie Ward、Jennifer Welch,他们也讲授过这门课程。还要感谢Damian Dechev、Tracy Hammond、Arne Tolstrup Madsen、Gabriel Dos Reis、Nicholas Stroustrup、J. C. van Winkel、Greg Versoonder、Ronnie Ward和Leor Zolman对本书初稿提出的建设性意见。感谢Mogens Hansen为我解释引擎控制软件。感谢Al Aho、Stephen Edwards、Brian Kernighan和Daisy Nguyen帮助我在夏天躲开那些分心的事,完成本书。
感谢Art Werschulz,他在纽约福特汉姆大学的课程中使用了本书第1版,并据此提出了很多建设性的意见。还要感谢Nick Maclaren,他在剑桥大学使用了本书的第1版,并对本书的习题提出了非常详尽的建议。他的学生在知识背景和专业需求上与德州农工大学大一学生有巨大的差异。
感谢Addison-Wesley公司为我安排的审阅者Richard Enbody、David Gustafson、Ron McCarty和K. Narayanaswamy,他们基于自身讲授C++课程或大学计算机科学导论课程的经验,对本书提出了宝贵的意见。还要感谢我的编辑Peter Gordon为本书提出的很多有价值的意见以及极大的耐心。我非常感谢Addison-Wesley公司的制作团队,他们为本书的高质量出版做出了很多贡献,他们是:Linda Begley(校对),Kim Arney(排版),Rob Mauhar(插图),Julie Nahil(制作编辑),Barbara Wood(文字编辑)。
感谢本书第1版的译者,他们发现并帮助澄清了很多问题。特别是,Lo?c Joly和Michel Michaud在法语翻译版中做了全面的技术检查,修改了很多问题。
我还要感谢Brian Kernighan和Doug McIlroy为撰写程序设计类书籍定下了一个非常高的标杆,以及Dennis Ritchie和Kristen Nygaard为实用编程语言设计提供的非常有价值的经验。
引 言
Programming: Principles and Practice Using C++, Second Edition
当实际地形与地图不符时,相信实际地形。
——瑞士军队谚语
讲授和学习本书的方法
我们是如何帮助你学习的?又是如何安排学习进程的?我们的做法是,尽力为你提供编写高效的实用程序所需的最基本的概念、技术和工具,包括程序组织、调试和测试、类设计、计算、函数和算法设计、绘图方法(仅介绍二维图形)、图形用户界面(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.
更全面的参考文献列表可以在本书最后找到。
作者简介
Programming: Principles and Practice Using C++, Second Edition
你也许有理由问:“是一些什么人想要教我程序设计?”那么,下面给出作者的一些生平信息。Bjarne Stroustrup和Lawrence“Pete”Petersen合著了本书。Stroustrup还设计并讲授了面向大学一年级学生的课程,这门课程是与本书同步发展起来的,以本书的初稿作为教材。
Bjarne Stroustrup
我是C++语言的设计者和最初的实现者。在过去大约40年间,我使用C++和许多其他程序设计语言进行过各种各样的编程工作。我喜欢那些用在富有挑战性的应用(如机器人控制、绘图、游戏、文本分析以及网络应用)中的优美而又高效的代码。我教过能力和兴趣各异的人设计、编程和C++语言。我是ISO标准组织C++委员会的创建者,现在是该委员会语言演化工作组的主席。
这是我第一本入门性的书。我编著的其他书籍如《The C++ Pro-gramming Language》和《The Design and Evolution of C++》都是面向有经验的程序员的。
我生于丹麦奥尔胡斯一个蓝领(工人阶级)家庭,在家乡的大学获得了数学与计算机科学硕士学位。我的计算机科学博士学位是在英国剑桥大学获得的。我为AT&T工作了大约25年,最初在著名的贝尔实验室的计算机科学研究中心——Unix、C、C++及其他很多东西的发明地,后来在AT&T实验室研究中心。
我现在是美国国家工程院的院士,ACM会士(Fellow)和IEEE会士。我获得了2005年度Sigma Xi(科学研究协会)的科学成就William Procter奖,我是首位获得此奖的计算机科学家。2010年,我获得了丹麦奥尔胡斯大学最古老也最富声望的奖项Rigmor og Carl Holst-Knudsens Videnskapspris,该奖项颁发给为科学做出贡献的与该校有关的人士。2013年,我被位于俄罗斯圣彼得堡的信息技术、力学和光学(ITMO)国立研究大学授予计算机科学荣誉博士学位。
至于工作之外的生活,我已婚,有两个孩子,一个是医学博士,另一个在进行博士后研究。我喜欢阅读(包括历史、科幻、犯罪及时事等各类书籍),还喜欢各种音乐(包括古典音乐、摇滚、蓝调和乡村音乐)。和朋友一起享受美食是我生活中必不可少的一部分,我还喜欢参观世界各地有趣的地方。为了能够享受美食,我还坚持跑步。
关于我的更多信息,请见我的网站www.stroustrup.com。特别是,你可以在那里找到我名字的正确发音。
Lawrence“Pete”Petersen
2006年年末,Pete如此介绍他自己:“我是一名教师。近20年来,我一直在德州农工大学讲授程序设计语言。我已5次被学生选为优秀教师,并于1996年被工程学院的校友会选为杰出教师。我是Wakonse优秀教师计划的委员和教师发展研究院院士。
作为一名陆军军官的儿子,我的童年是在不断迁移中度过的。在华盛顿大学获得哲学学位后,我作为野战炮兵官员和操作测试研究分析员在军队服役了22年。1971年至1973年期间,我在俄克拉荷马希尔堡讲授野战炮兵军官的高级课程。1979年,我帮助创建了测试军官的训练课程,并在1978年至1981年及1985年至1989年期间在跨越美国的九个不同地方以首席教官的身份讲授这门课程。
1991年我组建了一个小型的软件公司,生产供大学院系使用的管理软件,直至1999年。我的兴趣在于讲授、设计和实现供人们使用的实用软件。我在乔治亚理工大学获得了工业管理学硕士学位,在德州农工大学获得了教育管理学硕士学位。我还从NTS获得了微型计算机硕士学位。我在德州农工大学获得了信息与运营管理学博士学位。
我和我的妻子Barbara都生于德州的布莱恩。我们喜欢旅行、园艺和招待朋友;我们花尽可能多的时间陪我们的儿子和他们的家庭,特别是我们的孙子和孙女Angelina、Carlos、Tess、Avery、Nicholas和Jordan。”
令人悲伤的是,Pete于2007年死于肺癌。如果没有他,这门课程绝对不会取得成功。
目 录
Programming: Principles and Practice Using C++, Second Edition
第15章 容器和迭代器
简单练习
思考题
术语
习题
附言
第16章 算法和映射
简单练习
思考题
术语
习题
附言
第17章 一个显示模型
简单练习
思考题
术语
习题
附言
第18章 图形类 85
18.1 图形类概览 85
18.2 Point和Line 87
18.3 Lines 88
18.4 Color 91
18.5 Line_style 93
18.6 Open_polyline 95
18.7 Closed_polyline 96
18.8 Polygon 97
18.9 Rectangle 99
18.10 管理未命名对象 102
18.11 Text 104
18.12 Circle 105
18.13 Ellipse 107
18.14 Marked_polyline 108
18.15 Marks 110
18.16 Mark 111
18.17 Image 112
简单练习 114
思考题 115
术语 115
习题 116
附言 116
第19章 设计图形类 117
19.1 设计原则 117
19.1.1 类型 117
19.1.2 操作 118
19.1.3 命名 119
19.1.4 可变性 120
19.2 Shape 121
19.2.1 一个抽象类 122
19.2.2 访问控制 123
19.2.3 绘制形状 125
19.2.4 拷贝和可变性 127
19.3 基类和派生类 128
19.3.1 对象布局 130
19.3.2 类的派生和虚函数的定义 131
19.3.3 覆盖 131
19.3.4 访问 133
19.3.5 纯虚函数 134
19.4 面向对象程序设计的好处 135
简单练习 136
思考题 136
术语 137
习题 137
附言 138
第20章 绘制函数图和数据图 139
20.1 简介 139
20.2 绘制简单函数图 139
20.3 Function 143
20.3.1 默认参数 143
20.3.2 更多例子 144
20.3.3 lambda表达式 146
20.4 Axis 146
20.5 近似 148
20.6 绘制数据图 152
20.6.1 读取文件 153
20.6.2 一般布局 154
20.6.3 数据比例 155
20.6.4 构造数据图 156
简单练习 158
思考题 159
术语 159
习题 159
附言 160
第21章 图形用户界面 161
21.1 用户界面的选择 161
21.2 “Next”按钮 162
21.3 一个简单的窗口 163
21.3.1 回调函数 164
21.3.2 等待循环 166
21.3.3 lambda表达式作为回调函数 166
21.4 Button和其他Widget 167
21.4.1 Widget 167
21.4.2 Button 168
21.4.3 In_box和Out_box 169
21.4.4 Menu 170
21.5 一个实例 170
21.6 控制流反转 173
21.7 添加菜单 174
21.8 调试GUI代码 178
简单练习 179
思考题 179
术语 180
习题 180
附言 181
第22章 理念和历史 182
22.1 历史、理念和专业水平 182
22.1.1 程序设计语言的目标和哲学 182
22.1.2 编程理念 183
22.1.3 风格/范型 188
22.2 程序设计语言历史概览 190
22.2.1 最早的程序设计语言 191
22.2.2 现代程序设计语言的起源 193
22.2.3 Algol家族 197
22.2.4 Simula 203
22.2.5 C 204
22.2.6 C++ 207
22.2.7 今天 209
22.2.8 参考资料 210
思考题 211
术语 212
习题 212
附言 213
第23章 文本处理 214
23.1 文本 214
23.2 字符串 214
23.3 I/O流 217
23.4 映射 218
23.4.1 实现细节 222
23.5 一个问题 224
23.6 正则表达式的思想 225
23.6.1 原始字符串常量 227
23.7 用正则表达式进行搜索 228
23.8 正则表达式语法 229
23.8.1 字符和特殊字符 230
23.8.2 字符集 230
23.8.3 重复 231
23.8.4 子模式 232
23.8.5 可选项 232
23.8.6 字符集和范围 233
23.8.7 正则表达式错误 234
23.9 使用正则表达式进行模式匹配 235
23.10 参考文献 239
简单练习 239
思考题 239
术语 240
习题 240
附言 241
第24章 数值计算 242
24.1 简介 242
24.2 大小、精度和溢出 242
24.2.1 数值限制 245
24.3 数组 245
24.4 C风格的多维数组 246
24.5 Matrix库 247
24.5.1 矩阵的维和矩阵访问 248
24.5.2 一维矩阵 250
24.5.3 二维矩阵 252
24.5.4 矩阵I/O 253
24.5.5 三维矩阵 254
24.6 实例:求解线性方程组 255
24.6.1 经典的高斯消去法 256
24.6.2 选取主元 257
24.6.3 测试 257
24.7 随机数 258
24.8 标准数学函数 261
24.9 复数 262
24.10 参考文献 263
简单练习 264
思考题 264
术语 265
习题 265
附言 266
第25章 嵌入式系统程序设计 267
25.1 嵌入式系统 267
25.2 基本概念 269
25.2.1 可预测性 271
25.2.2 理想 272
25.2.3 生活在故障中 272
25.3 内存管理 274
25.3.1 动态内存分配存在的问题 274
25.3.2 动态内存分配的替代方法 276
25.3.3 存储池实例 277
25.3.4 栈实例 278
25.4 地址、指针和数组 279
25.4.1 未经检查的类型转换 279
25.4.2 一个问题:不正常的接口 280
25.4.3 解决方案:接口类 282
25.4.4 继承和容器 285
25.5 位、字节和字 287
25.5.1 位和位运算 287
25.5.2 bitset 290
25.5.3 有符号数和无符号数 292
25.5.4 位运算 295
25.5.5 位域 296
25.5.6 实例:简单加密 297
25.6 编码规范 301
25.6.1 编码规范应该是怎样的 302
25.6.2 编码原则实例 303
25.6.3 实际编码规范 307
简单练习 308
思考题 308
术语 310
习题 310
附言 311
第26章 测试 312
26.1 我们想要什么 312
26.1.1 警告 313
26.2 程序正确性证明 313
26.3 测试 313
26.3.1 回归测试 314
26.3.2 单元测试 315
26.3.3 算法和非算法 320
26.3.4 系统测试 325
26.3.5 寻找不成立的假设 326
26.4 测试方案设计 327
26.5 调试 328
26.6 性能 328
26.6.1 计时 329
26.7 参考文献 331
简单练习 331
思考题 331
术语 332
习题 332
附言 333
第27章 C语言 334
27.1 C和C++:兄弟 334
27.1.1 C/C++兼容性 335
27.1.2 C不支持的C++特性 336
27.1.3 C标准库 338
27.2 函数 338
27.2.1 不支持函数名重载 338
27.2.2 函数参数类型检查 339
27.2.3 函数定义 340
27.2.4 C++调用C和C调用C++ 341
27.2.5 函数指针 343
27.3 小的语言差异 344
27.3.1 struct标签名字空间 344
27.3.2 关键字 345
27.3.3 定义 345
27.3.4 C风格类型转换 347
27.3.5 无类型指针的转换 347
27.3.6 枚举 348
27.3.7 名字空间 348
27.4 自由存储空间 349
27.5 C风格字符串 350
27.5.1 C风格字符串和const 352
27.5.2 字节操作 352
27.5.3 实例:strcpy() 353
27.5.4 一个风格问题 353
27.6 输入/输出:stdio 354
27.6.1 输出 354
27.6.2 输入 355
27.6.3 文件 356
27.7 常量和宏 356
27.8 宏 357
27.8.1 类函数宏 358
27.8.2 语法宏 359
27.8.3 条件编译 360
27.9 实例:侵入式容器 360
简单练习 365
思考题 365
术语 366
习题 366
附言 367
附录C 标准库概要 368
附录D 安装FLTK 410
附录E GUI实现 413
术语表 419
参考文献 423