keep_window_open();
return1;
}
catch(...){
cerr<<"Oops:
unkownexception!
\n";
keep_window_open();
return2;
}
}
[编程题]
1.摄氏温度与开氏温度的转换(ex5-2、ex5-3、ex5-4、ex5-5)
2.[补充]e4-6_2.cpp、e4-7_3.cpp
Chapter6编写一个程序
1.[了解]基本哲学:
程序设计就是问题理解!
(p22、p100)
2.[理解、重点、难点]软件开发的几个阶段(§6.2.1)
●分析:
主要是确定做什么(whattodo)?
即问题是什么?
具体地,收集用户需求、评估可行性、根据用户需求设计一些测试用例。
●设计:
从策略上(宏观战略层面)考虑如何做(howtodo)?
具体地,为了解决问题(即达到需求分析中的功能目标),系统应具有怎样的总体结构?
(系统由哪些功能模块构成?
这些模块之间又如何交互?
)
●实现:
从细节上(微观战术层面)实施如何做(howtodo)的详细步骤?
具体地,将设计里的各个模块自顶向下、逐步求精细化到代码层面、再进行调试和测试。
因此,实现包含了编码、调试和测试。
值得注意的是,这些阶段并不是简单的瀑布型流程,而是会反复经历。
这里有个关键的概念是反馈(§1.6p22):
测试有助于改正和改进代码、编码中的问题可能表明设计有问题、设计的过程中也可能会发现分析中的疏漏和矛盾之处、系统的实际使用通常会暴露分析中的问题。
反馈是高效率软件开发的根本。
不仅如此,反馈的概念在硬件设计中也极为重要。
比如:
用于构建CPU寄存器、存储器等硬件的时序电路本质上=组合电路+反馈。
又例:
I/O设备中断的概念在计算机体系结构的发展演化过程中是一场革命,而中断概念的核心思想就是反馈。
3.[了解]在本章里,作者推荐了一种较实用的软件开发策略:
1)弄清问题,并作可行性评估
2)将大任务分解为易于处理的小任务
3)原型系统开发方法(本质上,就是为了尽早获得反馈,哪怕最初版本只是问题的部分解决方案):
a)先设计并实现一个受限版本,但能解决问题的关键部分;在开发的早期,避免“功能蔓延”很重要。
(P109L2)
b)再在此基础上,逐步扩充完善,最后得到完整的解决方案。
分阶段实现一个大程序比一次完成要简单得多。
(P109L3)
4.[了解、重点、难点]实用编程技术:
token(单词)及tokenize(词法分析)
5.[了解、重点、难点]实用编程技术:
grammar([补充]:
文法的语法图表示以及BNF范式表示)及analyzer(语法分析)
6.[重点]用户自定义类型(user-definedtype,简称UDT)和class关键字。
为了保证软件的可维护性,本课程强调的程序设计理念是代码必须直接体现要表达的思想,也即用代码直接描述问题域(即应用领域)中的概念(§1.6,p21L-4)。
这导致的直接后果是程序员常常发现编程语言提供的内建类型(built-intype)不能直接满足要求,须想方设法创建自己所需的类型,为此高级语言通常都提供创建用户自定义类型(user-definedtype)的机制。
在C++语言中,用以创建用户自定义类型(UDT)的最主要的关键字是class。
C++的这种做法体现了一种“积木块”的思想,语言本身只提供少数通用的内建类型,程序员可把它们当作“积木块”搭建自己所需的任何UDT。
这再次体现了机制与策略的分离:
语言提供构建UDT的机制(各种内建类型及class关键字),程序员提供如何定义UDT的策略(一般是直接将问题域的概念映射为相应的UDT)。
教材中的类Token、类Token_stream都是很好的UDT范例。
7.[了解、重点、难点]数据成员和成员函数以及public和private。
基本上,数据成员对应“类型”里“值”的概念,而成员函数则对应“类型”里“操作”的概念。
至于访问控制符public和private的引入,其实是区分了类型的用户和实现者这两种不同的角色:
UDT的用户只关心逻辑上它是什么?
它提供哪些服务?
这就是所谓的公有接口,它反映了用户感兴趣的部分,这些内容应放入class的public区;UDT的实现者则不同,他必须考虑如何才能将UDT实现出来以提供用户所要求的公有接口?
为了达到此目的,他可能需要定义相关的数据结构和辅助函数,此所谓的私有实现,这些反映了实现UDT所需的而对用户透明(即用户完全不关心)的东西,它们应该放入class的private区。
教材中的类Token_stream是很好的设计范例。
8.[了解、重点、难点]构造函数及函数重载。
构造函数是一种特殊的成员函数,它用于新生对象的初始化。
它有一些显著的特征:
没有返回值;函数名与类名相同,等等。
一个类可以定义多个构造函数,这就引出了函数重载的概念。
函数重载指的就是若干个函数,它们的名字相同,但参数特征(参数的类型或数目或顺序)不同。
教材中的类Token是一个好的范例。
9.[了解]函数的互递归调用及前置声明(forwarddeclare):
当几个函数出现相互调用的情形时,必须有函数要作前置声明(§6.9,p128调用关系图)。
[编程题]
1.ex6-2
2.ex6-5
3.ex6-9
4.ex6-10
Chapter7完善一个程序
1.[理解、重点]“完善程序”的概念:
在软件初步版本的基础上,对程序作进一步的完善和优化。
具体地,包括:
改进用户接口(界面)、测试(早测试、常测试)、增强错误处理——实用系统还应该具有一定的错误恢复能力(即,容错能力)、清理代码(目的是让代码清晰可读,具体措施有:
使用符号常量代替魔数、使用函数封装功能相对独立的操作、调整代码格式、完善注释等)、添加新功能等举措。
2.[理解、重点]测试(§7.3):
当完成程序的初步版本后,你应该做的第一件事是:
尽力让程序崩溃——也就是给它各种输入(合法的,非法的——尤其是刁难的、极端情形的输入)期望它表现出错误的行为。
记住:
尽早测试,经常测试(P141L-13)。
另外,也不应指望系统地测试就一定能发现软件系统中的所有错误。
关于这点,伟大的计算机科学家Dijkstra有句名言:
“测试只能证明程序有错,而不能证明程序没有错误!
”。
3.[理解、重点、难点]错误处理(§7.7):
我们应尽力避免在错误处理的过程中产生新的错误(类比:
医生在给病人做心脏手术的过程中自己心脏病突发!
)。
因此,一般用于“清理遗留故障”的错误恢复代码会偏于底层。
4.[理解、重点、难点]清理代码(§7.6):
目的是使代码清晰可读(即易于理解)。
具体措施有:
使用符号常量代替“魔数”;函数的设计所遵循正交性原则:
每个函数只做单一的逻辑操作(即独立的逻辑功能),不同的函数做互不重叠的事。
这样的好处:
降低了系统的复杂度,改进了可理解性、可维护性;注释的要点:
①最好的注释就是让代码本身来表达其意图。
因此,代码的清晰性对于程序的理解和维护都非常重要;②注释的抽象度要高于代码,一般用于代码本身很难表达思想的情形。
换言之,代码说明了它做什么?
而注释则表达了代码的意图。
清理代码时很重要的一点是,要让注释与代码相一致。
5.[理解、重点、难点]添加新功能(§7.8)。
要点是:
①尽力避免在开发的早期陷入“功能蔓延”,分阶段实现比一次完成所有功能要简单得多;②添加新功能时,要综合衡量用户需求和开发代价(开发期限、难度、成本等)两方面的诸多制约因素,避免提供对用户“华而不实”的功能。
6.[了解、重点、难点]实用编程技术:
错误恢复技术——计算器如何从错误的输入中恢复过来(§7.7)。
最主要的是“对症下药”——分析输入错误会影响哪个数据结构(比如,对于计算器程序而言,“Token_streamts;”就是这样的数据结构,所有的计算——文法分析函数都基于此数据结构)?
数据结构如何清除错误状态?
7.[了解、重点、难点]实用编程技术:
符号表(symboltable)——跟踪变量的机制(§7.8.1)。
借助符号表,我们最终为计算器添加了定义变量和符号常量这两个比较cool的功能。
[编程题]
1.Ex7-1
2.Ex7-3
3.Ex7-4
4.实验一:
设计并实现支持任意次回退操作的单词流。
Chapter8函数相关的技术细节
1.[理解、重点]声明和定义。
声明(declaration)作用:
将名字引入作用域;定义(definition):
所声明实体完整描述的声明。
广义上,定义也是一种声明,而狭义上,声明指“不是定义的声明”,两者的区别反映了接口(如何使用一个实体)与实现(该实体如何完成它的功能)的分离。
C++中,标识符(变量名、符号常量名、函数名、类型名)的使用原则:
先声明,后使用。
变量在首次使用前必须先被正确地初始化,符号常量必须在定义时就初始化。
定义只能一次,声明可以多次。
2.[理解、重点、难点]头文件(header)。
头文件中通常放的是变量、函数等的声明,UDT的定义通常也放在头文件中。
为防止头文件重复包含时引起类型重复定义的错误,通常我们对头文件要做一次包含处理。
有一种良好的编程风格:
将接口的声明、实现及使用相分离。
具体地,将全局变量、函数(全局函数或类的成员函数)的声明放在一头文件中,将其定义(尤其是函数的实现)放在一个C++源文件中,而客户端代码为包含该头文件的另一个C++源文件。
这体现了模块化的思想:
模块的接口/界面与实现相分离,而外界(客户端代码)通过其提供的接口来访问模块(即获取模块的服务)。
这就是所谓C/S模式,即客户(Client)/服务器(Server)模式。
其实核心思想就是:
(各个模块)接口与实现的分离、机制与策略的分离——各基本模块只提供每个模块的基本服务的机制(包括设计接口及实现接口),而客户代码(尤其是主框架代码)则决定如何综合利用这些接口来解决