C++程序设计语言(李雁妮) 第7章

上传人:E**** 文档编号:89345540 上传时间:2019-05-23 格式:PPT 页数:64 大小:702.50KB
返回 下载 相关 举报
C++程序设计语言(李雁妮) 第7章_第1页
第1页 / 共64页
C++程序设计语言(李雁妮) 第7章_第2页
第2页 / 共64页
C++程序设计语言(李雁妮) 第7章_第3页
第3页 / 共64页
C++程序设计语言(李雁妮) 第7章_第4页
第4页 / 共64页
C++程序设计语言(李雁妮) 第7章_第5页
第5页 / 共64页
点击查看更多>>
资源描述

《C++程序设计语言(李雁妮) 第7章》由会员分享,可在线阅读,更多相关《C++程序设计语言(李雁妮) 第7章(64页珍藏版)》请在金锄头文库上搜索。

1、第7章 名字空间与异常处理,7.1 模块与接口的基本概念 7.2 名字空间 7.3 异常处理 7.4 综合示例 小结 练习题,7.1 模块与接口的基本概念 任何一个设计、构造良好的实用程序均是由若干模块(Module)/构件(Component)组成的,即使它是一个相对简单的程序。这意味着: (1) 可以将程序划分成一组部件。 (2) 程序模块的划分可以从多个角度来进行:一是按自己写的程序与它所调用的系统支撑功能(如标准库函数或类库)来划分;二是将自己写的程序本身按某种原则(例如按功能)来进行划分,等等。 程序划分是有目的的。将程序划分成模块,不仅能使其程序结构更加清晰,而且其模块更易于被替换

2、与重用。,C+的名字空间(Namespace)和异常处理机制(Exception Handing)就是支持程序模块化组织的强有力机制。 本章我们首先以小型桌面计算器为例,来具体阐述C+用于支持模块化程序设计范型的主要机制名字空间及相关问题。 逻辑上,我们可将小型桌面计算器按功能划分为以下五大模块。 (1) 语法分析器(Parser):完成语言的语法分析; (2) 词法分析器(Lexer):完成语言的词法分析; (3) 符号表(Symbol Table):存储用户标识符的名-值对;,(4) 驱动程序(Driver):main函数; (5) 错误处理器(Error Handler):进行程序的错误

3、处理。 其中,语法分析器、词法分析器和符号表是该程序的三大核心部件。 程序中各模块之间的划分及模块间的相互关系如图7.1和图7.2所示。图中的箭头表示模块之间的调用关系。 通过前面的学习我们已经知道,仅需了解一个函数接口的具体定义,而无需了解函数的具体实现,我们就能够很好地使用函数。,图7.1 桌面计算器的程序逻辑划分,图7.2 桌面计算器的模块关系图,类似地,即使程序的一个部件是由多个函数组成的,或者其中既有自定义类型也有全局变量、还有函数,我们都可以这样来设想:如果这样的部件也像函数那样有一个起包装作用的接口,也同样可以只需要了解接口而不需要了解实现,就能够很好地使用它,这正是信息隐藏原理

4、的实质与宗旨。 以此理念,图7.2中程序各模块之间调用的依赖关系我们可用图7.3描述。,图7.3 程序各模块调用依赖图,图7.3中各个模块均调用错误处理模块,考虑图的清晰性,各模块对错误处理模块接口的调用我们暂未画出。 从图7.3可看出,各模块调用时直接依赖的仅仅是所调用模块的接口,而与其调用的实现无关。 确切地说,若程序中的一个部件具有明确的边界,能够实现接口与实现的分离,并对它的用户而言在使用时只需关心其接口而不管其实现,该部件就叫做模块(Module)。 实现模块的接口与实现的分离,需要程序设计语言提供相应的支持机制。C+提供的支持机制是Namespace和Class。 模块用接口隐蔽了

5、其中的数据和函数的处理细节(这也称做封装,Encapsulation),使得模块可以在保持接口不变的前提下,可改变其数据结构和函数的处理细节。,7.2 名 字 空 间 7.2.1 名字空间的基本概念 C+中的名字空间(Namespace)是一种表现逻辑聚集关系的机制。换句话说,如果一些声明(定义声明与非定义声明)在逻辑上都与某个划分准则有关,就可以把这些声明放入一个共同的名字空间中,以表现这一事实。 同一名字空间中的声明在概念上属于同一个逻辑实体。由于程序中的模块就属于这种逻辑实体,因而我们可用C+的名字空间来封装模块,将程序进行模块化组织。,C+的名字空间的语法形式为 namespace 名

6、字空间名 /逻辑相关的数据、函数、类或其它等 /注意:无“;”号 参照图7.1和第6章桌面计算器的代码,若以模块化的组织形式重构桌面计算器,则其语法分析(parser)模块为 namespace Parser double expr(bool); / 非定义声明 double prim(bool get) /* */ ,上面,我们仅仅是把一些逻辑相关的部分组织到一个名字空间中(模块),并未实现模块接口与其实现的分离。如何实现这种接口与实现的分离呢?关键是在其模块实现部分中出现的成员被该名字空间的名字所约束(Qualified),这样的约束通过约束符( Qualifier :,C+的作用域解析符

7、)来表示。以桌面计算器的Parser模块为例,进行界面/接口与实现分离的程序代码如下:,从上述代码可看出,接口与实现分离的要点是在每个模块接口中仅需非定义声明其中成员的接口部分,其模块成员的实现(如数据、类的定义、函数的定义等)均应以 名字空间名:成员 / 的形式进行名字空间外的定义声明。C+语法规定,利用此形式只能进行名字空间内成员的定义声明,而不能利用此语法新定义声明一个名字空间成员。,7.2.2 名字空间中的名字解析 名字空间既是一个封装体,也是一种作用域。一个名字空间中的名字(符号名)只能在其所定义的名字空间中可见、可用。 名字空间的另一主要用途是将不同的符号名组织到不同的名字空间中,

8、以避免名字冲突。 名字冲突在大型软件的开发过程中是一个突出问题。为了避免这个问题,在C+软件开发过程中,我们应有效地利用名字空间机制对软件中的名字进行有效的组织与管理。例如,开发中,我们可将自己的代码组织到一个命名的名字空间中,以避免和系统类库、其他程序员编制的程序中的名字发生冲突。,在以上两个名字空间mfc和owl中,虽然都有同名的变量名inflag,但由于它们属于不同的名字空间,因此不产生名字冲突。 在另一个名字空间中解析其他名字空间的名字,可采取 名字空间名:名字 的方式进行。例如: mfc:inflag=3; /mfc中的inflag owl:inflag=-256; /owl中的in

9、falg 当一个应用程序中有多个模块(或名字空间)时,由于各个namespace之间经常会出现互相使用对方成员的情况,如果每次使用就需用名字空间名进行约束,将会导致程序书写既繁琐又容易出错。例如,在Parser模块的term实现中,就用了一系列的名字解析(约束):,为了避免这种繁琐易错的名字解析,C+提供了几种“有限的统一”约束机制。,1using声明语句 语法:using名字空间名:名字; 语义:将某一名字空间中的名字引入(Introduce)到一个局部范围内,使其名字在该范围内无需名字空间的约束便可见、可用。 例如,在Parser模块中,由于使用了若干using声明语句,则在其namesp

10、ace中我们就可直接采用其他namespace的名字。示例代码如下:,需要注意的是,若using声明语句处于某一namespace的界面/接口中,则其using声明语句有效于(作用于)该namespace的所有实现。例如,若我们在Parser名字空间中采用了如下所示的using声明语句:,则在parser模块中的所有实现(prim、term和expr)中,get_token、curr_tok和error无需名字空间名约束便直接可见。我们还是以上述的prim实现为例:,我们可看出,在上述代码中所出现的其他namespace中的名字一律无需约束(解析),便能直接采用。,2using指令语句 语法:

11、using namespace 名字空间名; 语义:将特定名字空间中的所有名字引入到一作用域内。 我们已在前面各章节的示例程序中,多处见到了using namespace std;语句。该语句的功用即将系统std名字空间的所有名字引入(展现)到程序中。 值得注意的是,在一个namespace接口中使用using指令语句,其作用域为其namespace的所有实现中;若using指令语句用于某一个namespace的实现中,则其作用域为该实现内。若我们在namespace的Parser中采用了如下using指令语句:,namespace Parser double prim(bool); doub

12、le term(bool); double expr(bool); using namespace Lexer; /using指令语句 using namespace Error; /using指令语句 仍以Parser:prim为例,则在prim中出现的所有Lexer、Error模块中的名字均无需其名字空间名的约束而直接呈现(显露)给prim。请看其代码:,实际应用中,是采用using声明语句只将声明的名字引入另一区域,还是用using指令语句将其名字空间中的所有名字引入到另一区域,应具体情况具体分析。 需要切记的是:C+中的namespace是一个范围。采用namespace,我们可以对程

13、序进行逻辑上的组织与划分,程序越大,namespace的功能将愈强。 理想情况下,一个名字空间应该具有以下特性: (1) 是一个描述了具有逻辑统一性特征的相关成员的集合; (2) 实现了接口与实现的分离,对用户隐藏了namespace中的细节;,(3) 采用恰当的using声明语句或using指令语句,让用户免去任何明显的名字描述负担。,7.2.3 模块的多重接口 采用C+的名字空间机制,我们可将程序以模块化的形式组织起来。每一个模块应向调用者只暴露接口,而隐藏其实现。 一个模块通常有不同类别的用户。对不同的用户而言,一个模块应向他们提供不同的接口。例如,某模块的开发者能够涉及模块的全部成员,

14、而这个模块的普通用户则只应当涉及对应于对外功能的那些成员。因此,如果能够为不同的用户提供不同的接口,则该程序既友好又容易控制。,又由于面向开发者的接口变动的可能性通常会比面向普通用户的接口更大,从这个意义上来讲,为他们提供不同的接口,有利于隔离内部变动对外界的影响。因此,软件开发中,一个模块应为不同的用户提供不同的模块接口。 我们仍以桌面计算器为例来说明此问题。参看7.1节的图7.1,对于Parser模块而言,面对开发者,我们应提供该模块内所有成员的接口,而对于Driver模块而言,则只需提供Parser模块中的expr函数的接口即可,因Driver只需看到expr的接口。 C+语法上允许为一

15、个名字空间定义多个同空间名的接口。例如,对桌面计算器的Parser模块而言,为Driver可定义如下接口:,namespace Parser double expr(bool); 对Parser的实现者(开发者)而言,亦可定义如下同空间名的接口: namespace Parser double prim(bool); double term(bool); double expr(bool); using Lexer:get_token; /using声明语句 using Lexer:curr_tok; /using声明语句 using Error:error; /using声明语句 ,但实际的软件开发中,若需要一个模块向外提供多个接口,最好的实现方法是为每一个接口起一个namespace名,实际上采用这种实现方法,可以减少不必要的依赖和名字冲突。因此,就上述问题而言,为Driver和Parser的开发者所提供的两个接口定义分别如下: namespace Parser_interface /Driver的接口 double expr(bool); ,C+的名字空间机制还有一些相对复杂、高级的用法,在此我们不作赘述,有兴趣的读者可参阅C+标准和相关书籍。,7.3 异 常 处 理 对于一个实用的程序而言,没有错误处理是不可能的。实际的(大型)程

展开阅读全文
相关资源
相关搜索

当前位置:首页 > 高等教育 > 大学课件

电脑版 |金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号