资源预览内容
第1页 / 共90页
第2页 / 共90页
第3页 / 共90页
第4页 / 共90页
第5页 / 共90页
第6页 / 共90页
第7页 / 共90页
第8页 / 共90页
第9页 / 共90页
第10页 / 共90页
亲,该文档总共90页,到这儿已超出免费预览范围,如果喜欢就下载吧!
资源描述
Visual C+ Program Design第七章 程序结构 Chapter 7 Program Structure Visual C+ Program Designv程序结构: 使程序得以运行的框架组织便是程序结构,对程序结构的研究,是为了更好地表达算法思想,使其符合编译逻辑,又具有更好的可读性和可维护性v学习目标: 1. 从简单的函数层层调用,初步理解+程序结构 2. 学习合理组织程序的规则与经验,掌握扩展程序规模的基本方法Visual C+ Program Design第七章内容第七章内容1. 函数组织函数组织( Function Organization ) 2. 头文件头文件 ( Header Files ) 3. 全局数据全局数据 ( Global Data ) 4. 静态数据静态数据 ( Static Data )5. 作用域与生命期作用域与生命期 ( Scopes & Lifetime ) 6. 名空间名空间 ( namespace ) 7. 预编译预编译 ( Pre-Compilation ) Visual C+ Program Design7.1.1 程序构成程序构成函数:对输入参数负责,埋头做自己的事,最终返回结果函数组织:通过在函数中进行函数调用来扩展运行的规模,层层叠叠的函数构成树结构做法:将若干个函数组织成文件,又将若干个文件构成程序的办法来进行编程分工Visual C+ Program Design对如下的函数调用关系,进行文件划分对如下的函数调用关系,进行文件划分mainf2f1f3g1g2hp文件文件文件图7-2 程序结构的描述Visual C+ Program Design7.1.2 程序文件拆分程序文件拆分v在把握总体模块结构的基础上,一般是一个函数一个函数地编写,如果有很多函数,就要分好类,分成几个程序文件来由几个程序员负责。如图7-2的程序结构,八个函数可以从一个文件拆分成三个程序文件实现,如图7-3:v函数总是声明在前,调用在后。v为了实现多文件结构的编程,必须在程序工程中将这些编辑好的文件加入。Visual C+ Program Design/函数声明 void f1(); void f2(); void f3();/函数定义 int main() f1(); f2(); f3(); void f1()void p(); void f1()void f1();int main() void f1()void g1(); void g1()图7-3 一个文件拆分成三个程序文件Visual C+ Program Design7.2 头文件头文件原始头文件:作为共同开发的项目,为了共享彼此的过程资源(函数),将全体函数声明放在一个共用的头文件中界面头文件:界定模块可用资源(函数,数据,类型等)(可由一个或几个头文件组合,其实现由他人提供),或提供他人使用的模块资源它是由软件工程师分发的、以规范项目开发为目的的资源文件做法:练习划分函数组,模仿学习构造头文件,并注意头文件的应含内容Visual C+ Program Design7.2.1 原始头文件原始头文件v函数声明只能有惟一的一次,为了方便,可以将一个程序中所有要用到的函数专门做成一个程序文件,加在每个程序文件的开始处,也就是将那些函数声明做成一个文件后缀为.h的文件 (称之为头文件),用指令#include的方式加在程序文件的开始处。v如在前面的多文件结构中可以先做一个abc.h的头文件,其内容为:Visual C+ Program Design/ abc.hvoid f1 ( );void f2 ( );void f3 ( );void g1 ( );void g2 ( );void p ( );void h ( );原始头文件原始头文件 (包含图中的一切函数声明包含图中的一切函数声明)Visual C+ Program Design头文件的使用:使函数调用免于声明/ a1.cpp#include”abc.h”void f1() if() p(); g1(); else g2(); h(); Visual C+ Program Design头文件的使用:使函数调用免于声明/ a2.cpp#include”abc.h”int main() f1(); f2(); f3();/-void f3() f1();/-void p() f3();/-Visual C+ Program Design头文件的使用:使函数调用免于声明/ a3.cpp#include”abc.h”void h() void f2() g1(); g2();/-void g1() void g2() Visual C+ Program Design7.2.2 界面头文件界面头文件v上述的规定和使用头文件的方法是很原始的,头文件更重要的作用是在设计阶段规定界面,也就是通过头文件可以明白地看出,某个程序文件提供了什么服务,这种头文件称为用户界面。Visual C+ Program Design界面头文件界面头文件/ a1.h a1.cpp提供的资源void f1();/ a2.h a2.cpp提供的资源void p();/ a3.h a3.cpp提供的资源void g1();void g2();void f2();void h(); Visual C+ Program Design/ a1.cpp#include”a2.h”#include”a3.h”void f1() if() p(); g1(); else g2(); h(); 使用界面头文件Visual C+ Program Design/ a2.cpp#include”a1.h”#include”a3.h”static void f3();int main() f1(); f2(); f3();void f3() f1();void p() f3();使用界面头文件Visual C+ Program Design/ a3.cpp#include”a3.h”void h() void f2() g1(); g2();void g1() void g2() 使用界面头文件Visual C+ Program Design7.2.3 头文件的内容头文件的内容v头文件的作用是给源程序提供可以使用的外部资源一览表,它不仅仅包括函数声明,它基本上还包括:Visual C+ Program Designv全局数据声明, 如 extern int n; extern int a;v函数声明, 如void fn();v类型声明, 如class A;v全局常量定义,如const float pi=3.14;v内联函数定义,如inline void fn() ;v模板声明和定义,如template class A;v名空间定义,如namespace Nv类型定义,如enum COLOR; class A;v预编译指令,如#includev注释,如/2003年5月7日创建Visual C+ Program Designv由于头文件可能出现在一个程序的若干个源程序文件中,所以将一些实体定义放在头文件中是不明智的,因为一种定义体在一个程序中只能出现一次,如:Visual C+ Program Designv#includev int main()v std:cout“hello world!n”; vv然后,在源文件a.cpp中,写上v#include”abc.h”v编译运行,虽然能得到结果,但是这从根本上背离了头文件在C+程序结构体系中的作用!Visual C+ Program Designv头文件一定不能包括:v全局数据定义,如int a;v函数定义,如void fn();Visual C+ Program Design7.3 全局数据全局数据全局数据:使若干个模块在程序范围内共享(读与写)数据,是若干程序文件沟通数据的一种形式意义:模块的独立性由数据的封闭性来支持全局数据破坏了数据的封闭性,因而对小程序简单而对规范化程序则不登大雅之堂做法:练习函数之间用参数传递数据的常规形式,尽量避免使用全局数据Visual C+ Program Design7.3.1 全局数据访问全局数据访问v全局数据就是在任何函数的外部声明或定义的,在程序范围内可以访问的数据。v对于大多数函数都要访问某个数据的情形时,可以将它设置为全局的,就可以免于参数传递。Visual C+ Program Design例如:对于矩阵的输入、处理和输出例如:对于矩阵的输入、处理和输出vectorvector a; / global Datavoid input ( );void transpose ( );void print ( );int main ( ) input ( ); / using a transpose ( ); / using a print ( ); / using aVisual C+ Program Design7.3.2 消除全局数据消除全局数据v全局数据破坏了模块结构的独立性,也破坏了抽象数据结构的封闭性。程序是各个独立模块的聚集,全局数据牵扯了各个模块,使其无法独立。v可以通过参数传递的方法消去全局数据。如:Visual C+ Program Design消去全局数据:前一个过程的输出作为后一个过程的输入消去全局数据:前一个过程的输出作为后一个过程的输入typedef vectorvector Mat ; Mat input ( ) ;Mat transpose ( const Mat& a) ;void print ( const Mat& a ) ;int main ( ) print ( transpose ( input ( ) ) ) ;Visual C+ Program Design7.3.3 一次定义原则一次定义原则v全局数据有全局变量、全局常量、全局对象等,它们都有指针、引用、数组和其他形式。全局数据在程序存储结构中置身于全局数据区的位置。v全局数据区的整个区域在程序启动时,初始化为0.Visual C+ Program Designv在多文件结构的程序中,像单文件那样的全局数据定义形式存在着问题,因为在不同源程序文件中定义同名全局数据,就是多次构建全局数据实体,它意味着各个程序文件实际上使用的是各不相同的实体,如:Visual C+ Program Designv/ 多文件结构中全局数据的冲突v/=v#includev/-vint n=8;vvoid f();v/-vint main()v std:coutnn;v f();v/=8Visual C+ Program Designv/=v#includev/-vint n;vvoid f()v std:coutnn;v/=0Visual C+ Program Designv程序中的两个函数显示一个全局整型变量,却出现了不一致,原因是全局数据也应像函数那样,多次声明,而只能有一次定义。v全局数据的声明形式是在全局数据定义形式前加关键字extern,如:Visual C+ Program Design在多个程序文件组成的程序中共享数据,要遵守一次定义规则在多个程序文件组成的程序中共享数据,要遵守一次定义规则/ item1.cpp#includeusing namespace std ;int n = 8 ; / define void f ( ) ;int main ( ) coutn”n”; f ( ) ;/ item2.cpp#includeusing namespace std ;extern int n ; / declarevoid f ( ) coutn”n”; Visual C+ Program Designv还可以将各程序文件中的全局数据声明放在头文件中实现,如:v/=v/ f0705.hv/=vextern int n;vvoid f();v/=Visual C+ Program Designv/ f0705.cppv/=v#includef0705.hv#includev/-vint n = 8;v/-vint main()v std:coutnn;v f();v/=v/ f07051.cppv/=#includef0705.hv#includev/-vvoid f()v std:coutnn;v/=Visual C+ Program Design7.3.4 全局变量全局变量v全局常量也是放在全局数据区,只供读取,不许修改,不会扰乱模块关系。不允许在同一个程序中反复定义全局常量,但可以在不同的程序文件中重复定义。Visual C+ Program Designv全局常量可能被滥用,如求矩阵的和,其数据放在ab.txt中,每组数据前两个整数表示行和列,后面则是矩阵的元素,计算和然后输出。v在程序中,为了求若干个矩阵的和,先定义两个最大行列数的二维数组,然后反复重用,由于数组的下标只允许为常量,因此,设置全局变量,带来的后果是,占用了不必要的空间。若行列放宽后,则必须要改程序,带来了函数划分时所引入的参数传递困难,程序如下:Visual C+ Program Designv/=v#includev#includev#includevusing namespace std;v/-vconst int row=100;vconst int col=100;vint main()v ifstream in(ab.txt);v int marowcol;v int mbrowcol;v int mcrowcol;v v for(int r,c; inrc; )v for(int i=0; ir; +i)v for(int j=0; jmaij;v for(int i=0; ir; +i)v for(int j=0; jmbij;v for(int i=0; ir; +i)v for(int j=0; jc; +j)v mcij = maij + mbij;v for(int i=0; ir; +i)v for(int j=0; jc; +j)v coutsetw(4)mcij;v coutendl;v v Visual C+ Program Designv该程序可以完全不用全局常量,如下列程序可以表示了一种便于函数划分、增加函数独立性的常规方法,结构更加合理,不滥用空间,不滥用全局数据,运行结果与前面程序一致.Visual C+ Program Designv#includev#includev#includev#includevusing namespace std;v/-vtypedef vectorvector Mat;vvoid input(istream& in, Mat& a);vMat matAdd(const Mat& a, const Mat& b);vvoid print(const Mat& a);v/-vint main()v ifstream in(ab.txt);v for(int row,col; inrowcol; )v Mat a(row,vector(col);v Mat b=a;v input(in, a);v input(in, b);v print(matAdd(a,b);v v/-vvoid input(istream& in, Mat& a)v for(int i=0; ia.size(); +i)v for(int j=0; jaij;v/-vMat matAdd(const Mat& a, const Mat& b)v Mat c=a;v for(int i=0; ia.size(); +i)v for(int j=0; ja0.size(); +j)v cij += bij;v return c;v/-vvoid print(const Mat& a)v for(int i=0; ia.size(); +i)v for(int j=0; ja0.size(); +j)v coutsetw(4)aij;v coutendl;v Visual C+ Program Design7.4 静态数据静态数据静态全局数据:在一个程序文件中共享的数据注意:全局数据则在多个程序文件中共享数据静态局部数据:在屡次调用的同一个函数中共享的数据Visual C+ Program Design7.4.1 静态全局数据静态全局数据v在过程化程序设计中,为了使程序文件发挥模块的作用,有必要定义一种模块的局部量,它区别于其他程序文件,称之为静态全局数据(也称全局静态数据)。Visual C+ Program Designv函数的模块性在于只与其输入/输出联系,定义的变量在程序来说是局部变量,但在该函数内部来说,却是全局的。程序文件的模块性在于只与其定义的全局函数联系,只与包含文件联系,其定义的静态全局数据对于该程序文件内部来说是全局的,但对于整个程序来说却是局部的。Visual C+ Program Designv在程序工程中添加一个程序文件,等于添加了一个程序文件模块,意味着其他地方要使用该程序文件所定义的某些函数,在该程序文件中包含了某些头文件,意味着该程序文件要使用其他地方的函数。如根据图7-3的程序文件划分,其中 a2.cpp可以得到图7-4:Visual C+ Program Design a1.h a3.h程序文件模块 a2.cpp a2.h图7-4 程序文件模块 f1 f2 pVisual C+ Program Designv程序如下:v#includeabc.hv/-vint main()v f1();v f2();v f3();v/-vvoid f3()v f1();v/-vvoid p()v f3();v/-Visual C+ Program Designv程序中,有的函数是为文件中的其他函数服务的,并不对外提供服务,这些函数应声明为静态,表示局部于程序文件。同样有的变量只是为本文件服务,也不是全局数据,应标以static,这些函数和变量称为静态全局数据和静态全局变量,它只在本文件范围内可见,在其他程序文件中不可见。Visual C+ Program Designv没有静态全局常量,前述的全局常量可以看做是文件变量,每个程序文件都可以通过”const int a=3;”的形式来定义惟一的文件域全局常量,即静态全局常量,这就是为什么可以将全局常量写入头文件的原因。Visual C+ Program Design7.4.2 静态局部数据静态局部数据v函数中的变量为局部变量,在定义局部变量前面加上static,就成了静态局部变量,静态局部变量驻留在全局数据区,默认初始化值为0,而且不会受函数的调用和返回的影响,函数第一次被调用时,静态局部变量被建立,此后该变量一直存在,直到程序运行结束。Visual C+ Program Designv静态局部变量在函数内部定义,但却驻留在全局数据区,所以,从可见性来说,它与局部变量一致,从生命期来说,它与全局变量一致。v下面的程序演示了全局变量、静态局部变量和局部变量的区别:Visual C+ Program Designv#includevusing namespace std;v/-vvoid func();vint n=1;v/-vint main()v int a=0, b=-10;v couta=a, b=b, n=nendl;v func();v couta=a, b=b, n=nendl;v func();v/-vvoid func()v static int a=2;v int b=5;v a+=2, b+=5;v n+=12;v couta=a, b=b, n=nendl;v/= a =0,b=-10,n=1 a =4,b=10,n=13 a =0,b=-10,n=13 a =6,b=10,n=25 Visual C+ Program Designv由于静态局部变量是局部的,所以若把该变量的指针作为函数返回值到处传播是不妥的,而且也丧失了模块的独立性,因为任何依附于某个块的操作都必须依赖于某个块而不能独立。Visual C+ Program Design7.5 作用域与生命期作用域与生命期作用域:有很多种,变化最多的是局部作用域作用域遵守就近原则,它总是取用最贴近的名字,除非名字加前缀,则指特定区域的名字生命期:实体一旦产生(定义)后,存活时间的度量作用域与生命期:作用域是编程规范,用于编译时的语法检查,生命期是程序运行中的实体存活度量,体现运行程序的内在规律名字访问遵守作用域规则,而作用域以实体存活为前提Visual C+ Program Design7.5.1 作用域作用域vC+的作用域有全局作用域、文件作用域、函数作用域、函数原型作用域、类作用域和局部作用域。v作用域规则主要是针对程序文件而言,所以全局作用域就是文件作用域。v函数作用域即不管名称在函数的什么地方声明,总是可以在函数的任何位置先使用该名称。只有标号是函数作用域的,设置标号即对标号名称进行声明,常以”go to 标号;”的形式出现。Visual C+ Program Designv函数原型作用域,是表明函数声明时的形参与上下文无关,如: double width;/其中的width并未重复定义 void area(double width,double length); length =0; /错:length无定义正因为如此,函数声明中形参可以省略。因为它与上下文没有任何关系。类作用域与名空间机制类似。Visual C+ Program Designv局部作用域指在函数内部的动作序列描述中,依据各语句块甚至整个函数体范围内所定义的数据应遵循的数据访问规则。在局部作用域中,语句块往往是嵌套的,所以变量在其作用域并非一定可见,如果遇到更贴近的变量定义,则另一个外层同名定义将被暂时屏蔽。如:Visual C+ Program Designv/ 嵌套的局部作用域v/=v#includevusing namespace std;v/-vvoid fn(int y);vint j=8; / j为全局作用域v/-vint main()v int x=1;v fn(x);v/- / x作用域结束vvoid fn(int y) / y作用域开始v if(int i=1) / if语句块,i作用域开始v i=2*i;v elsev i=100;v / if语句块结束,则i作用域结束v int x=1; / x作用域开始v if(x y)v coutxendl;v elsev coutyendl;v /- / x作用域结束v switch(int i=2) / switch语句块,i作用域开始v case 1:v coutiendl;v / switch语句块结束,则i作用域结束v i = 3; / errorv int sum = 0; / sum作用域开始v for(int i=0; i10; +i) / i作用域开始v sum += i;v / i作用域结束 v int j=3; / fn函数块中,int j作用域开始v char ch; / fn函数块中,char ch作用域开始v v double j; / 本块中,double j作用域开始v j=5; / 虽赋整数于j,但仍然指double j,非int jv :j=6; / 全局变量通过:操作可见,但局部int j不可见v ch=A; / 只要本块中没有定义ch变量,则外块ch可见v / double j作用域结束v j=6.0; / int j可见v/= / j,ch,y,sum作用域结束Visual C+ Program Design7.5.2 生命期生命期v生命期指一个实体产生后,存活时间的度量。v静态局部数据的生命期并不与局部作用域一致,它的生命期一直延续到程序运行结束。Visual C+ Program Designv另有一种动态生命期,是由new申请到内存空间后,该空间实体开始有效,一直到delete语句释放该内存空间。在动态生命期中,其有效的堆空间实体可能被跨函数地访问,因此,其作用域是整个程序范围的。如:Visual C+ Program Designv/ 动态生命期v#includev/-vint* fn()v int* ap = new int; / ap所指向的内存空间开始有效,可以访问了v return ap;v/-vint main()v int* bp=fn();v *bp = 15;v std:cout*bp0?a:-a;v这个时候,就可以选择一个版本使用,如:Visual C+ Program Designv/ 解决名字冲突v/=v#includev/ using namespace std;v/-vint abs(int a) return a0 ? a : -a; v/-vint main()v int a = abs(-5);v int b = std:abs(-5);v std:coutastd:endlbstd:endl;v/=Visual C+ Program Designv如果标准库中的某个名称用的很频繁,又可能会遇到标准库的名称与自己定义的名字冲突,就可以局部默认名空间的名称使用,如上述程序可以改写为:Visual C+ Program Designv/ 局部名空间默认v/=v#includevusing std:cout;vusing std:endl;v/-vint abs(int a) return a0 ? a : -a; v/-vint main()v int a = abs(-5);v int b = std:abs(-5);v coutaendlbendl;v/=Visual C+ Program Design7.7 预编译预编译预编译:+在正式编译前的准备工作,它包括文件的加盟、代码的取舍、字符的替代等操作文件加盟由include指令引导,代码取舍由ifdef或ifndef引导,字符替代功能在逐渐退化,因为+语言本身完全可以胜任这项工作Visual C+ Program Designv常用的预编译指令:包含指令#include条件指令#if、#elif、#else、#endif、#ifdef、#ifndef定义指令#define、#undefVisual C+ Program Design7.7.1 #include指令指令v include指令指示预编译将包含的头文件的内容附加到程序文件中,以参加编译。v 如果头文件是C+系统提供的,则用尖括号把头文件括起来,如果是自定义的头文件,则用双引号把头文件括起来。v用户自定义的头文件一般放在源程序文件路径中Visual C+ Program DesignVisual C+ Program Design7.7.2 条件编译指令条件编译指令v条件编译的作用是直接取舍程序语句和协调多个头文件。v如在C+系统头文件中常有这样的编译指令:#ifdef _USE_OLD_RW_STL#include#else#include#endifVisual C+ Program Designv是为了兼容旧版C+头文件,如果C+的编译设置开关并不强调标准C+,则在预编译开始时预编译名称_USE_OLD_RW_STL就会已经声明,于是预编译到了此处,“ifdef”的编译条件为真,便执行#include编译指令,否则就执行#include编译指令。Visual C+ Program Designv条件编译指令#if以#endif结束,以#elif作为“否则如果”的递进条件,以#ifdef和#ifndef作为编译条件的两外两种形式。Visual C+ Program Design7.7.3 头文件卫士头文件卫士v条件编译指令的一种最常见的用法是头文件卫士,头文件卫士的目的是要保护嵌套包含指令中的内部链接属性的名称不被重复定义。如下列程序:Visual C+ Program Design头文件的可嵌套性,使得必须人为控制文件的展开操作,这就是头文件卫士的由头文件的可嵌套性,使得必须人为控制文件的展开操作,这就是头文件卫士的由来来(x.cpp中将出现二次中将出现二次struct Date定义定义)/a.h#include”b.h”/ /x.cpp#include”a.h”#include”b.h”/ /b.hstruct Date int year,month,day; ;Visual C+ Program Design若头文件中出现类或结构定义若头文件中出现类或结构定义,则应在其外面施加保护则应在其外面施加保护保护的方法是采用头文件卫士保护的方法是采用头文件卫士.以保证该类或结构定义只执行一次以保证该类或结构定义只执行一次/b.h #ifndef DATE #define DATE struct Date int year, month, day; ;#endifVisual C+ Program Design7.7.4 #define指令指令v#define指令称为宏指令。v用宏定义的函数,类似于inline函数,它不是真正的函数调用,而是在#define所定义的名称处插入一段代码。如:v #define MAX(a,b) (a)(b)?(a):(b)v#define ABC (“hello”)v int a =1,b=0;vMAX(a+,b);/a被加了两次,但若ba,则a只加1次vMAX(a,ABC);/错:类型不匹配Visual C+ Program Designv将对应的参数引入后,编译相当于做了:v (a+)(b)?(a+):b;v (a)(“hello”)?(a):(“hello”);Visual C+ Program Designv如果宏替换很复杂,则很难想象和理解原始代码,所以最好使用inline函数来代替宏函数定义。
收藏 下载该资源
网站客服QQ:2055934822
金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号