Download presentation
Presentation is loading. Please wait.
1
第6章结构化与面向对象程序设计基础
2
6.1 结构化程序设计的基本概念 6.1.1 程序 程序是一组能够完成特定任务的命令序列的集合。这些命令按照一定的逻辑顺序有机地结合在一起,并以文件形式存储在磁盘上,这种文件称为程序文件或命令文件,VFP中其扩展名为.PRG。执行程序时,计算机按程序的逻辑顺序自动执行程序文件中的命令。与在VFP的命令窗口逐条输入命令相比,采用程序方式的好处有: 可利用编辑器方便地输入、编辑和保存程序。 可采用多种方式、多次运行程序。 可以实现程序代码的复用。
3
【例6. 1】编写程序文件exam6_1. prg实现例4. 31。. 显示学生选课成绩表xkcjb
【例6.1】编写程序文件exam6_1.prg实现例4.31。 *显示学生选课成绩表xkcjb.dbf中当前记录的学号、课程号和成绩字段的值 CLOSE ALL &&关闭所有打开的文件 SELECT 2 USE dab &&选择2号工作区打开 dab表 SELECT C USE kcb &&选择3号工作区打开kcb表 USE xkcjb IN 5 &&在5号工作区打开xkcjb表 ? xkcjb.学号,b.姓名,xkcjb.课程号,xkcjb->成绩 CLOSE ALL &&关闭所有打开的文件 RETURN
4
在命令窗口中执行MODIFY COMMAND exam6_1命令后,弹出代码编辑窗口,在该窗口中输入上述程序代码并保存。在命令窗口输入:DO exam6_1,程序执行结果为:
从该例子可以看出:程序代码保存在一个程序文件exam6_1.prg中,可以多次执行。
5
6.1.2 算法及算法的表示 1)算法的概念及特点 要编制一个程序,必须首先构建解决问题的算法。所谓算法(Algorithm)就是对解决问题所采用的方法和步骤的描述。 【例6.2】输入3个数,找出其中的最大值并输出。 分析:假设这3个数分别用a、b、c表示,最大值用max表示。由于计算机一次只能比较2个数,首先可以将a和b进行比较,把其中大的数放入max中,再将max与c比较,又将大的数放入max中,最后将max输出。此时max中的值就是a、b、c中的最大值。
6
可以将求解这一问题的算法描述为: 第1步:输入a、b、c。 第2步:将a、b中大的一个数放入max中。 第3步:将c与max中大的数放入max中。 第4步:输出max。 从该例子可以看出,算法是对一个问题的解决方法和步骤的描述,是一个有穷规则的集合。一个算法应该具有以下特点: ①确定性。一个算法给出的每个计算步骤都必须是准确的、无二义性。在【例6.2】中每一个步骤的含义都是确定的。
7
②有穷性。一个算法必须在执行有穷多个计算步骤后终止。在【例6.2】中执行4个步骤后问题处理结束。
③有效性。算法中的每一个步骤必须能够有效地执行,并能够得到确定的结果。 ④输入。一个算法可以有0个或1个以上的输入。在【例6.2】中需要输入a、b、c 3个数。 ⑤输出。一个算法有1个或多个的输出,一个算法得到的结果就是算法的输出。在【例6.2】中输出的是max。
8
任何一个问题能否让计算机处理,关键在于能否设计出合理的算法,有了合适的算法,再使用合适的计算机语言来编写程序。VFP就是一种语言工具。要用VFP编写出高质量的程序,一方面要构建合理的算法,另一方面要掌握VFP的语法规则,它是实现算法的基础。
9
2)算法的描述 算法的描述方法有很多,如自然语言、伪代码、流程图等。为直观、准确地表示一个问题的处理方法和步骤,通常采用流程图的方法。流程图是用一些几何图形符号、线条及文字说明来表征一个问题的处理过程。一般采用传统流程图和N-S图来描述算法。实现【例6.2】的算法所对应的传统流程图如图(a)所示、N-S图如图(b)所示。
10
输入a,b,c 开始 将a,b中大的数放在max中 将c,max中大的数放在max中 输出max (b) (a)
11
顺序结构:是一种最基本、最简单的程序结构。如下图所示,先执行A,再执行B,A与B按照顺序执行。
3)3种基本结构的表示 1966年,Bohra和Jacopini提出了程序的3种基本结构:顺序结构、选择(或分支)结构、循环结构,它们构成了实现一个算法的基本单元。 顺序结构:是一种最基本、最简单的程序结构。如下图所示,先执行A,再执行B,A与B按照顺序执行。 A B (a)流程图 (b)N-S图
12
选择(或分支)结构:根据条件是否成立而执行不同的程序模块。在下图中,当条件P为真时,执行A,否则执行B。即要么执行A,要么执行B。
T F A B 条件p A B Y N (a)流程图 (b)N-S图
13
循环结构:是指重复执行某些操作,重复执行的部分称为循环体。如图所示为当型循环结构,当条件P为真时,反复执行A,直到条件P为假时才终止循环。其中A是循环体,A被重复执行的次数称为循环次数。循环结构除当型循环外,还有直到型循环。 条件P A N Y (a)流程图 (b)N-S图
14
结构化程序设计方法概述 结构化程序设计方法是一种自顶向下、逐步求精、模块化的程序设计方法。结构化程序设计方法的思想,就是将给定的一个功能较多的复杂问题合理地划分为若干个较简单的子问题,还可以将这些子问题再分解为若干功能更简单的子任务(可将每个子任务看作一个模块),以便于“分而治之”。如下图表达了一个较复杂问题的模块分解过程。 问题C1 问题A 问题B1 问题B2 问题Bn … 问题C2 问题Cn
15
在模块划分时,应该尽可能地保证数据的独立性和模块功能的唯一性。也就是说,各模块间的耦合性要尽量弱,即一个模块最好像是一个黑盒子,模块间的数据交换要尽量少;模块的内聚性要尽量强,即每个模块只完成单一的功能。 采用结构化程序设计方法的好处是:可以使每个模块功能明确,模块间的关系简单、清晰(即可读性强),每个模块内部的修改尽可能不影响其他模块(即独立性好),同时每个模块的正确性容易验证(即可维护性好)。 在VFP中,程序模块化是通过编写子程序、过程、函数来实现的。
16
6.2 程序文件的建立、编辑、修改与执行 6.2.1 程序文件的建立、编辑与修改
6.2 程序文件的建立、编辑、修改与执行 6.2.1 程序文件的建立、编辑与修改 在VFP中,建立和编辑程序文件可以采用多种文本编辑工具实现,这些文本编辑工具可以是VFP系统提供的内部编辑器,也可以是其他常用文本编辑软件。一般在代码编辑窗口中编辑程序代码。 在VFP系统环境下,建立和编辑程序文件有多种方法,在此主要介绍以下3种方式。
17
1)在项目管理器中创建程序文件 如果要让程序包含在一个项目文件中,可在项目管理器中建立程序文件。 在上图所示的【项目管理器】中,选择【代码】选项卡中的“程序”项,单击【新建】按钮,进入代码编辑窗口,输入程序内容,如右图所示。
18
2)用菜单方式新建程序文件 在【文件】菜单中选择【新建】菜单选项,在出现的【新建】对话框中选择“程序”文件类型,再单击【新建文件】按钮即可进入代码编辑窗口编辑程序代码。 如果要修改已有的程序文件,则在【文件】菜单中选择【打开】菜单项,这时弹出一个包含文件列表的对话框。在【文件类型】列表框中选择“程序”,然后在文件列表中选定要修改的程序文件,单击【确定】按钮即可。
19
3)用命令方式新建程序文件 格式:MODIFY COMMAND [<文件名>]|? 例如,在命令窗口中输入“MODIFY COMMAND search ”命令(如图6.8所示),即可打开代码编辑窗口。如果程序文件不存在,系统就自动建立一个指定名字的新程序文件;如果文件已存在,则将打开该程序文件。
20
用上述3种方法之一创建程序文件后,必须在代码编辑完毕时,保存文件。其方法是选择【文件】菜单下的【保存】或【另存为】命令进行保存;也可以利用“Ctrl+W”组合键进行保存,程序将保存在当前盘的当前路径下(注意:“Ctrl+Q”组合键放弃存盘)。
21
执行程序文件 程序文件创建之后便可执行,执行程序文件的方法有: ①若程序文件包含在一个项目中,则在【项目管理器】中,打开【代码】选项卡,展开“程序”项,选定程序文件,单击【运行】按钮。 ②在【程序】菜单中选择【运行】菜单项,在弹出的对话框中选定要运行的程序文件,单击“运行”按钮。 ③在【命令】窗口中,输入命令:DO <程序名> [WITH <参数表>]。
22
6.3 程序中常用命令 基本的输入/输出命令 输入/输出处理是计算机和用户打交道的接口,是程序设计过程中不可缺少的组成部分。处理过程中的原始数据或用户的要求都需要通过输入语句来输入,而处理结果又通过各种形式的输出语句来输出。在VFP中用于输入、输出的命令很多,在此只介绍几个常用的命令。
23
1)INPUT命令 格式:INPUT [<提示信息>] TO <内存变量> 功能:在屏幕上显示[<提示信息>],并等待用户通过键盘输入表达式,再把表达式的值赋给指定的<内存变量>。 例如:s=75 input to a ? "a=",a 执行上述命令,若输入的内容为s+20,则输出结果为: a=95 这表明变量a接收的是表达式的值。
24
格式: ACCEPT [<提示信息>] TO <内存变量>
功能:该命令和INPUT类似,不同的是它会把输入内容作为字符串常量赋值给指定的<内存变量>。 例如,把上例中的input改成accept: s=75 accept to b ? "b=",b 执行上述命令,若输入的内容为s+20,则输出结果为: b=s+20 这表明变量b接收的是字符串“s+20”。 说明:这两个命令中的<提示信息>可以缺省。当提示信息缺省的时候,则直接等待用户输入。
25
3)WAIT命令 格式:WAIT [[<提示信息>] TO <内存变量>] [WINDOW [AT <行>,<列>]][NOWAIT][CLEAR |NOCLEAR][TIMEOUT<数值表达式>] 功能:该命令的作用是暂停程序的执行,直到用户按任意键或单击鼠标或等待输入一个字符(可存入指定的内存变量)或在指定时间内继续程序的执行。
26
说明:在该命令中,若选用WINDOW,则会出现一个用于显示提示信息的WAIT提示窗口,通常位于主窗口的右上角。若需指定提示窗口的位置时,可使用AT实现;若同时选用NOWAIT和WINDOW,则程序不等待用户按键就继续执行;若选用NOCLEAR,则不关闭提示窗口,直到执行下一条WAIT WINDOW或WAIT CLEAR为止;若选用TIMEOUT,则在超过设定等待时间后程序自动继续执行。 例如:若在主窗口的10行、20列处显示提示信息“输入无效,请等待重新输入…”,10秒钟后自动关闭提示窗口,程序继续执行。可使用命令:WAIT“输入无效,请等待重新输入…”WINDOW TIMEOUT 10 AT 10,20。
27
4)基本输出命令?|?? 格式:?|??<表达式1>,[<表达式2>…] 功能:计算给定的一个或多个表达式的值,并将结果显示在屏幕上。 说明:“?”表示在当前光标的下一行输出,而“??”表示在当前光标位置处输出。
28
6.3.2 程序文件中的辅助命令 在程序中,有一些专门用于程序开始和结束、对程序进行注释以及环境设置的命令,常用的有: 1)注释命令 为了提高程序的可读性,编写程序时可在程序中适当加上一些注释。注释命令在执行时不做任何操作,也不影响程序的功能。 格式1:NOTE [<注释内容>] 格式2:&& [<注释内容>] 格式3:* [<注释内容>] 功能:用于在程序中加入说明,以增加程序的可读性。 说明:NOTE 和*命令用于行首注释,而&& 命令通常用于行尾注释。
29
2)程序结束命令 VFP的应用程序可以根据需要终止执行并返回命令窗口,返回操作系统或返回到调用它的上一级程序或主程序。 格式1:CANCEL 功能:结束当前 Visual FoxPro 程序的执行。 格式2:RETURN [TO MASTER] 功能:结束当前程序的执行,返回到调用它的上一级程序。带TO MASTER选项表示直接返回到主程序。 格式3:QUIT 功能:关闭所有文件,退出VFP系统,返回到操作系统。
30
3)清屏命令 命令格式:CLEAR 功能:清除屏幕内容。 4)SET TALK ON/OFF 功能:控制非输出性的结果是否在屏幕上显示出来。系统默认值为ON。 5)SET SAFETY ON/OFF 功能:确定在改写已有文件前,是否显示提示对话框。系统默认值为ON。
31
6.4 VFP程序的三种基本结构 6.4.1 顺序结构 顺序结构始终按照程序中语句的先后顺序逐条执行。
【例6.3】鸡兔同笼,已知鸡兔的总头数为h ,总脚数为f ,求鸡兔各有多少只? 鸡兔同笼问题的算法如图所示。 输入h,f x = ( 4*h-f ) / 2 y = ( f-2* h ) / 2 输出s
32
其程序文件exam6_3.prg的代码为: input “请输入鸡兔总头数:” to h input “请输入鸡兔总脚数:” to f x=(4*h-f)/ &&x为鸡的只数 y=(f-2*h)/ &&y为兔的只数 ?”共有鸡:”,x ?”共有兔:”,y cancel
33
选择结构 应用程序在进行数据处理时需要根据不同的条件选择执行不同的操作,使程序的流程根据不同条件来决定程序的走向,这种程序结构称为选择结构。在VFP中是用IF…ELSE…ENDIF和DO CASE…ENDCASE来实现的。
34
1)IF语句 格式: IF <条件表达式> <语句序列1> [ELSE <语句序列2>] ENDIF 功能:执行该语句时,若条件表达式的值为.T.,则执行<语句序列1>,否则执行<语句序列2>,然后再执行ENDIF之后的语句,如图所示。 条件表达式 T F 语句序列1 语句序列2 执行ENDIF之后的语句
35
说明: 在IF语句中,ELSE选项可缺省,此时在如上图中去掉<语句序列2>即可。当ELSE缺省时,如果条件表达式为.F.,则执行ENDIF后面的语句。 IF和ENDIF必须成对出现,否则程序将无法执行。 该语句可以嵌套使用,但是在嵌套使用时不能出现交叉现象。
36
【例6. 4】 输入球的半径,计算球的体积。 其程序文件exam6_4
【例6.4】 输入球的半径,计算球的体积。 其程序文件exam6_4.prg的代码为: clear input "请输入球的半径:" to r if r>0 v=(4/3)*3.1416*r^3 ? "球的体积是:",round(v,2) &&保留2位小数 else messagebox("输入半径不能为负数!") endif cancel
37
【例6.5】判断某一年是否是闰年。 分析:假如年份用year表示,判断year是否是闰年的方法是:year能够被400整除或year能够被4整除并且不能够被100整除 (用vfp表示的逻辑表达式为year/4=int(year/4) and year/100<>int(year/100) or year/400=int(year/400))。
38
其程序文件exam6_5.prg的代码为: clear input "请输入年份:" to year if year/4=int(year/4) and year/100<>int(year/100) or year/400=int(year/400) ?str(year,4)+"年是闰年" else ?str(year,4)+"年不是闰年" endif return 提示:判断一个整数M能否被另一个整数N整除,一般采用3种方法,即:判断MOD(M,N)是否等于0或INT(M/N)是否等于M/N或M%N是否等于0,若等于,则表明M能被N整除。
39
2)多路分支语句 格式: DO CASE CASE <条件表达式1> <语句序列1> [CASE <条件表达式2> <语句序列2> … CASE <条件表达式N> <语句序列N>] [OTHERWISE <语句序列Q>] ENDCASE
40
功能:从第一个CASE的条件开始判断,若<条件表达式I>(I=1、2、3…n)的值为. T
功能:从第一个CASE的条件开始判断,若<条件表达式I>(I=1、2、3…n)的值为.T.,则执行<语句序列I>(I=1、2、3…n),然后执行ENDCASE之后的语句。所以在一个DO CASE结构中,最多只能执行一个CASE语句后面的语句序列。如果所有CASE语句后的条件表达式的值均为.F.,则执行OTHERWISE之后的<语句序列Q>。OTHERWISE子句可以缺省,此时如果所有条件表达式的值均.F.,就退出该结构执行EDNCASE语句后面的语句。 说明: ①DO CASE、CASE <条件表达式I>、OTHERWISE和ENDCASE必须各占一行。 ②DO CASE和ENDCASE必须成对出现。 ③语句序列中可以嵌套各种控制结构的命令语句。
41
【例6.6】计算分段函数的值 分析:这是一个多分支结构的问题,实现方法很多,下面给出其中的2种。
42
方法1:用CASE语句实现 input "输入x的值:" to x do case case x<0 f=2*x-1 case x=0 f=4*x otherwise f=7*x-5 endcase ?"f(",x,")=",f return
43
方法2:用IF的嵌套结构实现 input "输入x的值:" to x if x<=0 if x<0 f=2*x-1 else
endif f=7*x-5 ?"f(",x,")=",f return
44
【例6.7】编写程序显示当前月份的季节。 其程序文件exam6_7.prg的代码为: yue=month(date()) &&获取当前月份值 do case case inlist(yue,3,4,5) &&第一个表达式的值是其余表 达式值之一就返回.T. jj="春" case inlist(yue,6,7,8) jj="夏" case inlist(yue,9,10,11) jj="秋" case inlist(yue,12,1,2) jj="冬" endcase messagebox("当前季节为:"+jj+"季") return
45
【例6.8】在学生选课成绩表xkcjb.dbf中,输入学生学号,根据该学生的成绩判断学生表现。若成绩≥85分输出“优秀”,若在70~85分输出“良好”,若在60~70分输出“及格”,60分以下则输出“不及格”。 其程序文件exam6_8.prg的代码为: use xkcjb accept "请输入学生的学号:" to xh locate for 学号=xh
46
if not found() ? "学号输入错误!" else do case case 成绩>=85 ?xh+"的成绩为:优秀" case 成绩>=70 and 成绩<85 ?xh+"的成绩为:良好" case 成绩>=60 and 成绩<70 ?xh+"的成绩为:及格" otherwise ?xh+"的成绩为:不及格" endcase endif use return
47
循环结构 循环是按照给定的条件去重复执行一段具有特定功能的程序。循环结构是一种重要的程序结构。在VFP中提供了DO WHILE…ENDDO、FOR…ENDFOR、SCAN…ENDSCAN三种循环处理命令。 1)DO WHILE循环 命令格式: DO WHILE <条件表达式> <语句序列> ENDDO 功能:当执行DO WHILE语句时,如果表达式的值为.T.,则执行<语句序列>,否则结束该循环,执行ENDDO之后的语句。 语句序列 条件 ENDDO之后的语句 假 真
48
求解该问题的算法所对应的传统流程图和N-S图如图所示。
开始 I=1 s=0 I<=100 s=s+I I=I+1 Y 输出s 结束 N I=1,s=0 (a)传流程图 (b)N-S图
49
程序文件exam6_9.prg的代码为:s=0 i=1 do while i<=100 s=s+I I=I+1 enddo ?”s=“,s cancel
50
【例6.10】逐条显示学生档案表dab.dbf中计算机1班学生的信息。
其程序文件exam6_10.prg的代码为: clear use dab do while .not.eof() &&当记录指针指到数据表尾则结束该循环 if 专业班级=“计算机1班” &&指定学生的班级 disp endif skip &&记录指针移向下一记录 enddo use cancel
51
2)FOR…ENDFOR计数循环 格式: FOR <循环变量>=<初值> TO <终值> [STEP<步长>] <语句序列> ENDFOR|NEXT 功能:当FOR语句中循环变量的值在“初值”和“终值”之间时,执行FOR与ENDFOR之间的循环体,否则退出循环,如下图所示。
52
说明: 当STEP(步长)缺省时,其默认值为1。
循环初值赋给循环变量 循环变量是否超过终值? 执行循环体中语句序列 循环体变量按步长变化 执行ENDFOR之后的语句 说明: 当STEP(步长)缺省时,其默认值为1。 当STEP 不为1时,则循环变量的值就按照步长的值自动增加或减少(步长大于0,则增加,此时,循环条件为循环变量<=终值;步长小于0,则减少,此时,循环条件为循环变量>=终值)。
53
【例6.11】求n的阶乘。 求解该问题的算法如图所示。 其程序文件exam6_11.prg的代码为: input "请输入n:" to n
fac=1 for i=1 to n fac=fac*i endfor ?n,"!=",fac return 输入n i=1,fac=1 i<=100 fac=fac*i i=i+1 输出s 求n!的N-S图
54
3)SCAN...ENDSCAN循环 格式:SCAN[范围] [FOR |WHILE <条件表达式>] <语句序列> ENDSCAN 功能:在当前数据表中扫描指定范围内满足条件的所有记录,找到一条满足条件的记录就执行一遍 <语句序列>,直到对所有满足条件的记录执行完为止。在命令中当范围缺省时为ALL。
55
【例6.12】利用SCAN语句在学生档案表dab.dbf中查找7月份出生的学生的学号、姓名、出生日期以及专业班级情况,并统计人数。
其程序文件exam6_12.prg set talk off clear use dab ?"学号"," 姓名","出生日期","专业班级" scan for month(出生日期)=7 ?学号,姓名,出生日期,专业班级 endscan count to rs for month(出生日期)=7 ?" 合计: ",rs,"人" use set talk on cancel
56
程序执行结果为: 学号 姓名 出生日期 专业班级 蜂雪燕 07/02/87 计算机1班 陈宗贵 07/02/87 英语3班 合计: 2人
57
4)循环结构中的2个特殊语句 在循环结构中可使用2个特殊语句LOOP和EXIT来改变程序的正常循环。如果在DO WHILE循环、FOR循环、SCAN循环语句中含有LOOP语句,当遇到了LOOP语句时,则忽略LOOP后面的语句序列,结束本次循环,去再次计算<条件表达式>的值;当遇到EXIT语句时,则无条件退出本层循环,执行ENDDO、ENDFOR、ENDSCAN之后的语句。如下图所示为DO WHILE循环中含有LOOP及EXIT语句的情况。
58
语句序列1 LOOP EXIT 语句序列2 条件 ENDDO之后的语句 假 真
59
说明:实现该问题的方法很多,该程序仅仅是为了说明EXIT和LOOP语句的使用,而不是最佳实现方法。
【例6.13】利用exit和loop语句编写程序计算100以内的奇数和。 其程序文件exam6_13.prg的代码为: s=0 i=0 do while .t. i=i+1 if i>100 exit &&退出循环 endif if i%2=0 &&如果i为偶数 loop &&结束本次循环 else s=s+i enddo ? "100以内的奇数和为:",s cancel 说明:实现该问题的方法很多,该程序仅仅是为了说明EXIT和LOOP语句的使用,而不是最佳实现方法。
60
5)多重循环结构 若在一个循环结构的循环体中又包含了循环,则该循环结构称为循环嵌套。在一个循环结构中按照嵌套层次的多少,可分为双重循环、三重循环等。嵌套层次在2层以上一般称为多重循环结构。按其所处的位置分别称为外循环与内循环,其中内循环结构的循环体称为内循环体,外循环结构的循环体称为外循环体,如下图所示。每一层循环结构可以采用DO WHLIE、FOR等语句,其执行过程,按照各自语句的含义执行。
61
外循环体 外循环结构 内循环结构 内循环体 (a)双重循环 (b)三重循环 EXIT
62
【例6.14】利用双重循环编程求sum=1+(1+2)+(1+2+3)+…+(1+2+3+…+10)之值。
其程序文件exam6_14.prg的代码为: clear sum=0 for i=1 to 10 t=0 for j=1 to i t=t+j endfor sum=sum+t ? "1+(1+2)+(1+2+3)+…+(1+2+3+…+10)=",sum return
63
【例6.15】利用xkcjb.dbf和dab.dbf,通过反复输入学生的学号(输入回车键退出),查询学生的姓名、专业班级、课程号以及成绩情况。
方法1:建立表间关联,并进行查找处理。 set safety off &&当产生已存在的文件或索引标记时,直接覆盖,不给出确认对话框 sele 1 use dab index on 学号 tag xh sele 2 use xkcjb set relation to 学号 into dab
64
do while .t. &&外循环 accept "请输入学生的学号:" to xh if len(alltrim(xh))=0 exit endif ? “姓名 专业班级 课程号 成绩" scan for 学号=xh &&内循环 ?dab.姓名,dab.专业班级,课程号,成绩 endscan enddo close all set safety on return
65
方法2:利用SELECT-SQL语言实现。
clear all do while .t. accept "请输入学生的学号:" to xh if len(alltrim(xh))=0 exit endif select 姓名,专业班级,课程号,成绩; from xkcjb,dab where dab.学号=xh and xkcjb.学号=dab.学号 to screen enddo
66
6.5 模块化程序设计 在编写应用程序时,往往会遇到一些比较复杂的问题。对这些问题的处理可以采用“自顶向下,逐步细化、模块化”的方法,把整体的功能需求逐步划分,通过简单而具有独立功能的子模块来实现。这样既可以保证模块的重复使用,也可以方便调试、节省存储空间。模块就是具有独立功能的程序段。在VFP中可以通过子程序、过程和自定义函数来实现模块的功能。
67
6.5.1 子程序 对于2个具有调用关系的程序文件,常把被调用的程序文件称为子程序,而调用它的程序称为主程序。子程序是指具有相对独立性和通用性的程序段,它是一个独立的程序文件(.prg),存放在磁盘上。 (1)子程序的建立、调用与返回 子程序的建立方法与程序文件的方法相同。执行指定子程序可用DO命令来实现。 格式:DO <文件名>
68
主程序与子程序的概念是相对的,子程序还可以调用它自己的子程序,即子程序可以嵌套调用。子程序的嵌套如下图所示。
…… DO A 子程序A DO B RETURN 子程序B RETURN TO MASTER
69
VFP的返回命令包含了因嵌套而引出的多种返回方式。 格式:RETURN[TO MASTER]|[TO <程序文件名>]
70
【例6.16】输入圆的半径r,求圆的面积。 程序代码如下: **主程序 ***exam6_16 .prg input "请输入圆的半径:" to r do sub1 &&调用子程序 return **子程序,独立于主程序的程序文件 **sub1.prg area=pi()*r^2 &&计算圆的面积,pi()返回π值 ? " 圆的面积为:",area return &&返回主程序
71
(2)带参数的子程序 由于模块的功能之间具有相关性,有时候需要从主程序向子程序传递数据,有时候子程序又要传递数据给主程序,这些数据称为参数。具有参数传递的子程序称为带参数子程序。 带参数子程序的开头必须是PARAMETERS参数语句,相应地,主程序的调用方式也有所不同。 在带参数子程序中参数语句的格式为: PARAMETERS <形式参数表> 说明:形式参数表又简称形参,必须是变量,用来接收主程序传递过来的参数,多个形参用逗号隔开。
72
主程序的调用命令是: DO <子程序文件名> WITH <实际参数表> 说明: ①实际参数又简称实参,是用于向子程序传递数据的参数,可以是常量、已赋值的变量、表达式或函数。 ②实参的个数、类型应与带参数子程序PARAMETERS语句中的形参个数、类型一致,但名字可以不同。 调用时,主程序将实参一一对应地传送给子程序PARAMETERS语句中的形参(主程序→子程序)。子程序返回时,若实参是变量,则接受对应的形参的值(子程序→主程序)。
73
【例6.17】用带参数子程序编写例6.16。 程序代码如下: ***主程序 ***exam6_17.prg area=0 input "请输入圆的半径:" to r do sub2 with r,area &&调用子程序时传递参数 ? "圆的面积为:",area return
74
***子程序 ***sub2.prg parameters a,b b=pi()*a^2 return 调用时,将主程序的实参变量r、area的值一一对应地传递给子程序中的形参变量a、b;返回时,又会将a和b的值分别传给r和area。这是一个双向参数传递(主程序r 子程序a,主程序area 子程序b)。
75
6.5.2 过程 子程序有一个弱点,每调用一次子程序,系统就要打开一个程序文件,当调用很多子程序或一个子程序被反复调用时,频繁打开文件的操作会增加磁盘开销,降低系统运行速度,同时也给系统开发带来了不便,为此,引入了过程。 过程是可命名的、具有独立功能的程序段,与子程序不同的是,过程可以不是一个独立的程序文件。它既可以与主程序同在一个文件中,也可以和其他过程一起单独组成一个文件,这就克服了调用子程序时反复打开文件的缺陷。
76
格式:PROCEDURE <过程名>
<语句序列> RETURN|ENDPROC 说明:PROCEDURE <过程名>是过程的开始,RETURN是过程的结束。过程可以与调用它的主程序放在同一个文件中,一般放在程序文件的末尾。 过程的调用与子程序的调用完全相同,即用DO <过程名>语句直接调用。
77
【例6.18】用过程编写例6.16计算圆面积。 程序代码如下: ***exam6_ 18.prg area=0 input "请输入圆的半径:" to r do sub3 with r,area &&调用过程 ? " 圆的面积为:",area return ***将过程直接放在主程序的后面 procedure sub3 &&sub3是过程名 parameters a,b b=pi()*a^2 return &&返回主程序
78
自定义函数 VFP为用户提供了大量的系统函数(第3章已经学习了很多的系统函数),同时也允许用户创建自己的函数以满足不同领域的特殊需要。用户自己创建的函数称为自定义函数。 与系统函数类似,自定义函数也是通过函数名返回函数值。
79
定义格式:[FUCATION <自定义函数名>]
[PARAMETERS <参数表>] <语句序列> RETURN <表达式> 调用格式为:<函数名>([<参数表>]) 说明: ①若使用FUNCTION语句来指出函数名,表示该函数包含在主程序中。若缺省该语句,表示此函数是一个独立的文件,函数名即程序文件名,其扩展名是.prg,并可使用命令MODIFY COMMAND <函数名>来建立或编辑自定义函数。
80
②自定义函数的函数名不能和VFP的系统函数同名,也不能和内存变量同名。
③[PARAMETERS <参数表>]定义了函数的参数,与带参子程序的参数用法相同。 ④<语句序列>组成为函数体,用于各种处理;简单的函数其函数体可以为空。 ⑤RETURN <表达式>将表达式作为函数值返回给主程序。
81
【例6.19】用自定义函数编写例6.16计算圆面积。 方法1:自定义函数作为一个独立的文件。 自定义求圆面积的函数area.prg如下: parameters a return pi()*a^2 &&函数体为空,直接将表达式的值返回 若要计算半径为10的圆面积,则在命令窗口中直接输入 ?area(10) &&显示结果:
82
过程文件 在实际应用中,常常把多个过程或自定义函数集中在一个程序文件中,这个文件称为过程文件。 在过程文件中,每个过程或自定义函数仍然是独立的,只是为了避免频繁读取磁盘,在主程序调用时,一次全部调入内存,从而提高了系统的运行效率。
83
(1)过程文件的格式 PROCEDURE <过程名1> <语句序列1> RETURN PROCEDURE <过程名2> <语句序列2> FUNCTION <函数名3> <语句序列3> FUNCTION <函数名4> <语句序列4> RETURN
84
(2)过程文件的建立 过程文件的建立与一般程序文件的建立方法一样。 (3)打开过程文件 格式:SET PROCEDURE TO <过程文件名> 说明:该命令在主程序中使用。在同一时刻只能打开一个过程文件,打开新的过程文件时原来的过程文件会自动关闭。 (4)关闭过程文件 格式:SET PROCEDURE TO 或 CLOSE PROCEDURE 说明:若要修改过程文件,必须先关闭它。
85
6.5.5 变量的作用域 VFP的内存变量除类型、取值外,还有一个非常重要的属性——作用域。变量的作用域是指变量在什么范围内是可见的、有效的和可操作的。在程序设计中,特别是模块化程序中,经常会遇到许多内存变量,这些内存变量有的在整个程序运行过程中有效,有的只在某些模块中有效,这就是内存变量的作用域。VFP中的内存变量按其作用范围可分为公共变量、私有变量和本地变量。
86
1)公共变量 公共变量是指在整个程序执行过程中任何模块内都有效的内存变量。 格式:PUBLIC <内存变量表> 功能:将<内存变量表>中的变量全部设置成公共变量,并把这些变量的初值赋为.F.。 说明: ①在<内存变量表>中的变量可以是简单变量,也可以是数组。 ②在VFP命令窗口中定义的变量默认为全局变量,但这样定义的变量不能在程序中使用。 ③程序执行完毕后,全局变量不会在内存中自动释放。只有使用RELEASE命令或CLEAR MEMORY命令才能清除公共变量。
87
2)本地变量 本地变量只能在定义它的模块中使用,不能在其他模块中使用。只要定义它的程序运行结束,本地变量将自动释放。本地变量用LOCAL命令建立。 格式:LOCAL <内存变量表> 功能:该命令是将<内存变量表>中的变量设置为本地变量,并为它们赋予初值.F.。 说明:本地变量要先定义后使用。 3)私有变量 在VFP程序中不通过PUBLIC或LOCAL命令事先声明的变量均为私有变量。私有变量只能在建立它的模块及其下属各级模块中有效,一旦建立它的模块运行结束,这些私有变量将自动释放。
88
【例6.20】 分析下面程序中变量的作用域。 其程序文件exam6_20.prg的代码为: clear all local k &&定义本地变量k i= &&定义私有变量i do sub4 ?′主程序中输出的结果:′ ?i,j,k &&显示2 3 .f. return
89
*子程序sub4 .prg public j &&定义公共变量j i=i*2 j=i+1 k=j+1 ?′子程序中输出的结果:′ ?′i=′+str(i,2)+′ j=′+str(j,2)+′k=′+str(k,2) &&显示i=2 j=3 k=4 return
90
4)变量屏蔽 在模块化程序设计中,各层模块中可能出现相同的变量名。为了避免在不同层次的模块中因为使用相同名字的变量而相互影响值的变化,可在子模块中使用PRIVATE命令屏蔽掉主模块或上级模块中同名的变量,使得这些变量在子模块中暂时无效。只要返回主模块或上级模块,原来同名变量会重新出现,并且其值保持不变。 格式:PRIVATE [<内存变量表>|ALL [LIKE|EXCEPT<通配符>]] 功能:该命令将当前模块中某些与上层模块中同名的变量隐藏起来,直到声明它的程序、过程或自定义函数执行结束后,才恢复使用先前隐藏的变量。
91
【例6.21】 主程序调用子程序过程中的变量屏蔽。
(1)程序代码如下: *主程序exam6_21.prg clear public i, j store 1 to i, j, k do sub5 ?′主程序的输出结果:′ ?′i=′+str (i,2)+′ j=′+str(j,2)+′ k=′+str(k,2) return
92
*子程序sub5.prg private j,k &&屏蔽主程序中的变量j、k i=i*2 j=i+1 k=j+1 ?′子程序中的输出结果:′ ?′i=′+str(i,2)+′ j=′+str(j,2)+′ k=′+str(k,2) return (2)执行主程序后的输出结果为: 子程序中的输出结果: i=2 j=3 k=4 主程序的输出结果: i=2 j=1 k=1
93
方法2:自定义函数与主程序在同一文件中。
***主程序exam6_19.prg input "请输入圆的半径:" to r ? " 圆的面积为:",area(r) &&调用自定义函数 return ***自定义函数,直接放在主程序的后面 function area &&area是自定义函数名 parameters a return pi()*a^2
94
6.6 程序调试与测试 程序设计完成后,除较简单的程序外,很少一次运行成功。通常在编译运行时,会发现许多错误或不能达到预期的结果。这就需要利用VFP的调试工具来调试程序,检查出程序中的错误位置及错误原因,并对其进行改正,直到满足要求为止。程序的调试通常先分模块调试,当各模块调试通过后,再将各模块合并在一起进行调试,通过联调后即可运行。 1)调试器窗口 在VFP的环境下,选择【工具】菜单中的【调试器】命令或在命令窗口中输入DEBUG命令,即可进入调试器窗口,如下图所示。
95
菜单栏 工具栏 跟踪窗口 监视窗口 调用堆栈窗口 局部窗口 调试输出窗口
96
(1)工具栏的功能 工具栏是一系列工具按钮的组合,是一种图形化的操作界面,具有直观快捷的特点。通过工具栏,可以迅速地使用常用的菜单命令。工具栏上的按钮通常与一些菜单命令相对应,提供了一种执行经常使用的快捷方法,调试器窗口工具栏按钮的含义,如下图所示。 打开文件 取销 继续执行 追踪 单步 跳出 运行到光标处 追踪窗口 监视窗口 局部窗口 调用堆栈窗口 输出窗口 切换断点 断点对话框 清除所有断点 切换编辑日记 切换事件追踪
97
(2)各子窗口的作用 追踪窗口:用于显示正在调试执行的程序。在调试过程中,追踪窗口中左侧灰色区域会出现一些标记:〖ZK)〗 箭头标记“ ”:表示程序执行的当前位置。选择【调试】菜单中的【单步】命令或单击工具栏中的【单步】按钮,系统就执行一条命令。每执行一次“单步”操作,系统就按程序的流程执行一条命令。 断点标记“●”:在调试过程中,经常需要程序执行到某个位置上停止,以便分析当前程序执行的情况,这个位置称为断点。设置断点的方法可在指定行左边双击或将光标移到要测试的行并按空格键(或按回车键)。
98
局部窗口:用于显示程序或过程中想要查看的变量、数组、对象等。
调用堆栈窗口:用于显示正在执行的过程或程序列表。如果一个程序被另一个程序调用,则两个程序的名字都显示在调用堆栈窗口中。 调试输出窗口:用于显示程序、过程或方法程序代码的输出。使用时可在必要的行添加DEBUGOUT命令。 格式:DEBUGOUT <表达式> 当程序执行到此命令时,会计算出<表达式>的值,并送到调试输出窗口显示。
99
2)程序调试实例 【例6.22】用调试器调试程序exam6_14.prg,要求设置断点和表达式,并观察在程序执行过程中表达式及变量值的变化情况。调试步骤如下: ①选择【工具】菜单中的【调试器】命令,进入调试器窗口。若在调试器窗口中没有出现跟踪、监视、局部窗口等,可单击【调试器】窗口中的【窗口】菜单,选择所需的窗口。 ②单击工具栏上的【打开】按钮或执行【文件】菜单中的【打开】命令,在弹出的【添加】对话框中,选择调试程序exam6_14.prg,确认后程序代码将显示在跟踪窗口中。
100
③在跟踪窗口中用鼠标双击代码行左边或将光标移到要测试的行并按空格键(或按回车键)设置断点。当程序执行到断点处,将暂时停止执行,以便用户观察。
④在监视窗口中设置监视表达式,以监视程序中变量、函数、表达式的值及变化情况。 ⑤单击工具栏上的【跟踪】按钮,系统从程序的第一行开始执行,每单击一次【跟踪】按钮,系统就执行一行程序;也可单击工具栏上的【继续执行】按钮或【单步】按钮执行程序,从而在监视窗口、局部窗口中观察变量值或表达式值的变化情况。可根据需要选择使用。如下图所示的“监视窗口”中加入了i、t、j、sum 4个变量,在程序的调试过程中,可以看到这些变量值的变化。
102
3) 调试程序中的错误 程序的调试是程序开发过程中一个非常重要的环节。特别是代码较长的程序,其调试工作量特别大,通常需要程序设计者经过多次调试,不断地发现并修改其中的错误。熟悉程序调试过程中的错误类型是提高程序调试效率的有效途径。程序的错误一般有2类,一类是语法、词法错误;另一类是程序的逻辑错误。
103
(1)语法错误 程序中的语法错误通常是由于读者对VFP的语法规则记忆不深、理解不够所导致。最常见的语法错误主要表现在:关键字、变量名和文件名拼写错误;表达式和函数中的类型不匹配;控制语句缺少结束语句或嵌套错误;遗漏关键字和变量之间的空格;内存变量没有初始化等。语法错误相对容易发现和修改,当程序运行遇到这类错误时,VFP会自动中断程序的执行,并弹出编辑窗口,显示出错的命令行,给出错误信息,可在代码编辑窗口中方便地对程序进行修改。
104
(2)逻辑错误 程序的逻辑错误通常是由于程序结构或实现问题的算法不正确导致的。它不仅难以发现,而且是致命的,将直接影响到程序的执行结果。这类错误系统是无法确定的,只有程序设计者自己来检查错误。因此,要求在程序调试过程中跟踪程序的执行,在动态执行过程中分析执行结果是否与设计要求相符。在检查程序逻辑错误的过程中,可通过设置断点,追踪程序的运行过程来发现错误,也可通过单步执行、跟踪程序执行过程中某些信息的变化或设置错误陷阱捕捉可能发生的错误。
105
6.7 结构化程序设计应用实例 1) 数字计算问题 【例6.23】求一元二次方程ax2+bx+c=0的根(不考虑复根)
6.7 结构化程序设计应用实例 1) 数字计算问题 【例6.23】求一元二次方程ax2+bx+c=0的根(不考虑复根) 求一元二次方程的根的公式为:
106
其算法的描述如下图所示。 输入a,b,c 计算disc=b2-4ac T N a≠0 disc≥0 输出无实数解 计算x1、x2
107
其程序文件exam6_23.prg代码为 input "请输入数据a:" to a input "请输入数据b:" to b
input "请输入数据c:" to c disc=b*b-4*a*c if a!=0 if disc>=0 x1=(-b+sqrt(disc))/(2*a) x2=(-b-sqrt(disc))/(2*a) ?"x1=" ,x1 ?"x2=" ,x2 else messagebox("没有实数解") endif messagebox("a不能为0",0+64,"一元二次方程的解") cancel
108
【例6.24】求任意两个数的最大公约数。 求最大公约数的算法及实例分析如图6.23所示。 m n r 16 24 8 输入m,n r=m%n
r!=0 T r=m%n m=n n=r F 输入m,n m n r 16 24 8
109
其程序文件exam6_24.prg的代码为 clea store 0 to m,n,r ?"求任意两个数的最大公约数" input "请输入第一个数:" to m input "请输入第二个数:" to n r=m%n do while r<> 0 m=n n=r enddo ?"最大公约数是:",n cancel
110
【例6.25】 定义一个判断n是否是素数的函数,然后调用该函数求100以内的所有素数。
分析:素数又称质数,只能够被1和它本身整除。如果要判断n是否是素数,就用2~(n-1)之间的数逐个去除,若余数都不为0,则n为素数,否则不是素数。为减少除的次数,提高运行效率,除到 即可。
111
* prime.prg判断n是否是素数的函数
PARAMETERS n flag=.t. &&flag为真时,表明n为素数 k=int(sqrt(n)) for j=2 to k if mod(n,j)=0 flag=.f. exit endif endfor return flag *main.prg调用该函数求100以内的素数 clear for m=2 to 100 if prime(m) ??m endif endfor cancel
112
【例6.26】 从键盘随意输入的10个数,然后按从大到小的顺序依次排列后输出。
其程序文件exam6_26.prg的代码为: clea dime x(10) **输入数据 for i=1 to 10 inpu "请输入第"+str(i,2)+"个数" to x(i) next
113
**以下双重循环是完成排序 for i=1 to 9 for j=i+1 to 10 if x(i)<x(j) t=x(i) x(i)=x(j) x(j)=t endif next **以下循环是输出排序结果 ?x(i) return
114
【例6.27】 (猜数游戏)由计算机随机产生一个整数,根据难易程度,可以是2位数、3位数或者更高。游戏者事先不知道它的值。游戏的任务就是去猜测这个数。如果游戏者猜测的次数越少,说明级别越高。为了提高游戏的趣味性,需要给游戏者提示一些信息,例如,随机数是x,游戏者猜测的数是y,如果y>x或者y<x应该给出提示。游戏能重复进行。在本例子中,随机数为2位。
115
其程序文件exam6_27.prg的代码为: =rand(-1) &&随机函数初始化 _screen.fontsize=18 &&设置桌面显示尺寸 do while .t. clear &&清屏 x=int(rand()*100) &&产生2位随机数 y= &&初始化猜测数 n= &&初始化次数
116
do while y<>x input '输入一个猜测数:' to y n=n+1 do case case y<x ?'您猜的数太小。' font '宋体',10 case y>x ?'您猜的数太大。' font '宋体',30 otherwise exit endcase ?'猜了第'+str(n,4),+'次' enddo
117
?'恭喜您,猜中了!!! 这是第'+str(n,4)+'次'
z='y' wait '继续猜数吗?(y/n)' to z if lower(z)<>'y' exit endif enddo
118
【例6.28】编写程序求1!+2!+…+100!的值。 其程序文件exam6_28.prg的代码为: s=0 n=1 do while n<=100 m=n do js with m s=s+m n=n+1 enddo ? "1! +2! +3! +…+100! =",str(s,20) return
119
procedure js parameter x b=1 for a=1 to x b=b*a endfor x=b return
120
2)数据库操作问题 【例6.29】 有数据表ascdb.dbf,有3个字段:NUM1(N,2)、CHR1(C,2)与CHR2(C,2)。编写程序,向ascdb.dbf添加100条记录,每个字段的内容分别为: NUM1:1,2,3,4,5,…,24,25,26,27,28,29,… CHR1:A,B,C,D,E,…,X,Y,Z,A,B,C,… CHR2:A,E,I,M,Q,…,O,S,W,A,E,I,… 注意:ASCII码到字符的转换函数为CHR(<数值表达式>),“A”的ASCII码为65。
121
其程序文件exam6_29.prg的代码为: clear use ascdb dele all pack num=0 asc1=64 asc2=61 do while num<=99 num=num+1 asc1=asc1+1 if asc1>=91 asc1=65 endif
122
asc2=asc2+4 if asc2>=91 asc2=asc2-26 endif append blank replace num1 with num replace chr1 with chr(asc1),chr2 with chr(asc2) enddo use cancel
123
【例6. 30】 在学生档案表(dab. dbf)和选课成绩表(xkcjb
【例6.30】 在学生档案表(dab.dbf)和选课成绩表(xkcjb.dbf)中,查询成绩<60分的学生的学号、姓名、专业班级、所学课程的课程号、学期、成绩和重考成绩。
124
方法1:建立表间关联,并进行查找处理。 其程序文件exam6_30.prg的代码为: set safety off close all clear select 1 use dab index on 学号 tag xh select 2 use xkcjb set relation to 学号 into dab
125
? "学号 姓名 课程号 学期 成绩 重考成绩" scan for 成绩<60 ?学号,dab.姓名,课程号,学期,成绩,重考成绩 endscan close all set safety on cancel 方法2:利用selectsql语言实现。 select dab.学号,姓名,课程号,学期,成绩,重考成绩from dab,xkcjb where dab.学号=xkcjb.学号and成绩<60 to screen
126
【例6.31】 设有学生档案表、成绩表和学生课程表的表结构如下:
学生档案表(xsda.dbf):学号(C,10),姓名(C,8),性别(C,2),入学总分(N(5,1))。 学生成绩表(xscj.dbf):学号(C,10)(有重复值),课程号(C,5)(有重复值),学期(C,1),考试成绩(N(5,1))。 学生课程表(xskc.dbf):课程号(C,5),课程名(C,14)。 按如下要求编写一个程序:
127
①根据以上3个表,显示所有学生各门课程的学号、姓名、课程名、考试成绩。
②通过键盘任意输入一个课程号和学期,计算并显示该门课程该学期的平均分、最高分和最低分,显示该门课程该学期所有需要参加补考的学生的补考通知单,其格式如下: 补考通知单 XXX同学: 第X学期你的XXXXXX课程考试不及格,请利用假 期看书复习,作好补考准备。 注意:补考通知单中的“X”需用数据表中的相应数据填充。
128
其程序文件exam6_30.prg的代码为 clear clear all set safe off set talk off select 1 use xsda inde on 学号 tag xh select 2 use xskc inde on 课程号 tag kch select 3 use xscj set relation to 学号 into xsda set relation to 课程号 into xskc additive ?"学号 姓名 课程名 考试成绩" scan ?学号,xsda.姓名,xskc.课程名,考试成绩 endscan
129
accept "请输入课程号:" to kch accept "请输入学期:" to xq calculate avg(考试成绩),max(考试成绩),min(考试成绩) to pjf,zgf,zdf for 课程号=kch and 学期=xq ?"该门课程该学期的平均分为:",pjf ?"该门课程该学期的最高分为:",zgf ?"该门课程该学期的最低分为:",zdf scan for 成绩<60 and 课程号=kch and 学期=xq ?" 补考通知单" ?xsda.姓名+"同学:" ?" 第"+学期+"学期你的"+xskc.课程名+"课程考试不及格,请利用假" ?"期看书复习,作好补考准备。" endscan close all set talk on set safe on return
130
6.8 面向对象程序设计 6.8.1 从结构化程序设计到面向对象程序设计
6.8 面向对象程序设计 6.8.1 从结构化程序设计到面向对象程序设计 在20世纪80年代之前,应用软件的开发一般都采用结构化的程序设计方法,又称为面向过程的程序设计或过程化程序设计方法。其基本思想是“自顶向下、逐步求精”,即将一个复杂的规模较大的应用软件划分为若干个相对独立的功能模块,然后对各个模块编写代码,最后通过模块间的调用实现软件系统的运行。结构化程序设计极大地提高了软件生产率,是程序设计的基石。但随着计算机应用的深入和发展,问题规模越来越大,软件复杂性也大幅度提高,使结构化程序设计产生了2个问题:
131
编程人员始终要考虑程序代码执行的顺序及条件,即程序的过程问题,往往陷于大量而且复杂的分支、循环等控制结构中,当程序的规模增大到一定程度时,程序过程的控制难以准确地实施,加大了程序编制的难度,程序的可靠性就降低了。 程序代码与处理的数据分离,一旦应用需求发生变化,花大量力气编制的程序却几乎不能重用或移植,因此代码可重用性低,不能有效地提高程序的开发效率。
132
针对以上问题,提出了面向对象的程序设计方法(Object Oriented Programming,OOP)。OOP是利用人们对事物进行分类的自然倾向,将一些公用的软件模块“类”化,由类引申出具体的对象。面向对象程序设计克服了面向过程程序设计的缺点,其基本思想是以数据为中心,将数据与程序封装于对象之中,淡化了解决问题的过程和步骤,有效降低了程序开发的逻辑复杂性,使程序易于理解和测试;另一方面,由于对象具有相对独立性和通用性,因而提供了代码复用的可能,提高了程序的开发效率。
133
值得强调的是:OOP是程序设计的新视角,它并没有代替结构化程序设计。VFP不仅支持面向过程的程序设计,同时还提供了面向对象程序设计的强大功能,使用户在开发应用软件时能获得更多的灵活性和更高的效率。
134
【例 6.32】计算圆面积。 在面向过程的程序设计中我们讨论了求解圆的面积,其过程代码和运行结果如下图所示。
135
现将其改为用面向对象程序设计方法来实现,其设计过程如下:
①新建表单Form1。 ②在表单上适当的位置添加3个用于显示提示和结果的标签控件Label1、Label2、Label3,分别设置其Caption属性为“请输入圆的半径:”、“圆面积:”和空格串,并设置Label2、 Label3的FontSize属性为20。 ③在表单上适当的位置添加1个文本框控件Text1,用于半径值的输入。 ④在表单上适当的位置添加1个命令按钮控件Command1,用于启动计算,设置其Caption属性为“计算圆面积”。
136
⑤在Command1的Click事件中添加如下代码:
R=val(thisform.text1.value) &&获得半径值 Thisform.label3.caption=alltrim(str(pi()*r*r)) &&显示计算结果,保留整数部分 ⑥保存表单到VFP的默认路径。 运行表单,输入半径值30,然后单击【计算圆面积】按钮,其运行结果如下图所示。
137
由此可见,面向对象程序设计方法简单、直观、实用、自然,接近人类处理问题的自然思维方式。VFP的面向对象程序设计的主要特点是:
编程方式是可视化的,所见即所得。 程序运行没有一定的顺序,由事件驱动,随事件的出现(如用户点击鼠标)而执行相应的代码。 不需要专门的循环结构就可以重复其功能。 大部分的软件部件,如窗口、按钮等已经由“类”实现,不需要重复编码,屏蔽了大多数复杂的程序代码。 应用软件的开发更注重于核心问题的求解。
138
有关对象的基本概念 对象(Object) 对象是OOP中最基本的概念。从可视化编程的角度来看,对象是一个具有属性、能处理相应事件、具有特定方法程序、以数据为中心的统一体。简单地说,对象是一种将数据和操作过程结合在一起的程序实体。因此,对象是构成程序的基本单位和运行实体 每个对象都有一定的静态特征,即对象的属性,如电话的形状、大小、颜色等就是其属性。每个对象都可以对一些动作进行识别和响应,如电话可以识别拿起、放下听筒等动作,称为对象的事件;每个对象也有自己的行为或动作,如电话拨号,称为对象的方法程序。
139
类(Class) 类是一组具有相同特性的对象的抽象,是将某些对象的相同的特征(属性和方法等)抽取出来,形成的一个关于这些对象集合的抽象模型。例如,在某表单中有功能不同的“计算”、“退出”等按钮对象,这些按钮都是由命令按钮(CommandButton)类创建的。 类与对象关系密切,但它们是不同的。类是抽象的,对象是具体的,有类才能产生对象,对象是类实例化的结果。对象是类的一个实例,具有所在类所定义的全部属性和方法。在VFP中,任何对象都是由类实例化得到.定义了对象的基本属性、事件和方法,从而决定对象的属性和行为。
140
因此类具有以下主要特征: 封装性——隐藏不必要的复杂性 封装就是指将对象的方法程序和属性代码包装在一起。例如,用于确定命令按钮外观、位置等的属性和鼠标单击该命令按钮时所执行的代码是封装在一起的。封装的好处是能够忽略对象的内部细节,使用户集中精力来使用对象的外在特性。 多态性——指允许相关的对象对同一消息做出不同的反应。 相同的操作可作用于多种类型的对象上,并获得不同的结果。
141
继承性——充分利用现有类的功能 如果一个类是从其他已有的类中派生出来的,则称为子类或派生类。子类保持了父类中的行为和属性,但增加了新的功能,体现了面向对象设计方法的共享机制。类的继承性使得在一个类上所做的改动反映到它的所有子类中。有了类的继承,在编写程序时,可以把具有普遍意义的类通过继承引用到程序中,并只需添加或修改较少的属性、方法,这种在父类中所作的添加或修改将自动反映到它的所有的子类上,这样可使用户在程序设计中节省大量的时间和精力,也可减少维护代码的难度。 类的继承方式有2种: 单一继承:一个派生类最多有一个父类。 多重继承:一个派生类有多个父类,它具有每个父类的属性和方法。
142
属性(Property) 是用来描述和反映对象特征的参数,对象中的数据就保存在属性中。例如,控件名称(Name)、标题(Caption)、颜色(Color)、字体(FontName)、是否可见(Visible)等都是对象的属性,这些属性决定了对象展现给用户的界面具有什么样的外观及功能。在VFP中,可以在“属性”窗口中修改对象的属性。
143
事件(Event) 是指预先设计好的、能够被对象识别和响应的动作。可以为事件编写相应的程序代码,这样,当某个事件发生时,该程序代码就会自动地被调用执行,从而达到程序控制的目的。事件是由系统预先定义的,不能修改、增加和删除。事件可以由一个用户的动作产生,如单击鼠标或按下一个键;也可以由系统产生,如计时器。 在例6.32中,命令按钮具有一个事件——鼠标单击(Click Event),并对其编写了事件代码,以计算圆的面积。
144
方法(Method) 是指对象自身可以进行的动作或行为。它实际上是对象本身所内含的一些特殊的函数或过程,以便实现对象的一些固有功能。可以通过调用对象的方法实现该对象的动作及行为。例如,通过“转向”方法使方向盘对象旋转,从而使车轮转向。VFP程序中每个窗口或控件对象,具有改变其行为或实现某个特定操作的方法,如窗口可以被“显示(Show)”或被“隐藏(Hide)”等。 在面向对象的程序设计过程中,可以为对象创建新的属性和方法程序。
145
6.8.3 面向对象的编程模型 面向过程的程序设计强调“过程”,通过对一系列过程的调用和处理来完成解题,整个程序的流程是程序设计者事先安排好的,即编写程序时要考虑好什么时候发生什么事情。 面向对象程序设计则是以“对象”为出发点,重点考虑执行程序设计功能的对象模型,着重于建立能够模拟需要解决问题的现实世界的对象。 在面向对象的程序设计中,对象是组成软件的基本元件。每个对象可看成是一个封装起来的独立元件,在程序里担负着某个特定的任务。因此,在程序设计时,不必知道对象内部细节,只是在需要时对对象的属性进行设定和控制,书写相应的事件代码即可。
146
对象和应用程序的关系如图所示。 数据代码 属性 事件 方法 应用程序 读取/设置 响应 调用 对象
147
由例6.32可知,面向对象的程序在执行时,先等待某个事件的发生,然后再去执行处理此事件的事件过程(即程序代码)。事件过程要经过事件的触发才会被执行,这种动作模式就称为事件驱动,也就是说,由事件控制整个程序的执行流程。执行步骤如下: ①等待事件的发生。 ②事件发生时,执行其对应的事件过程。 ③重复步骤①。 由此周而复始地执行,直到程序结束。 相应地,面向对象编程的基本过程是:先创建容器对象→定义数据环境→摆放控件对象→设置对象属性→为事件编写程序代码。
148
【例6.33】求解任意一个一元二次方程ax2+bx+c=0的实根。
设计要求:运行时,在窗口中恰当的位置输入一元二次方程的系数a、b、c的值,确认无误后单击“求解”按钮,立刻就会在窗口的适当位置显示结果。如果方程没有实根,则给出提示信息。
149
①创建容器对象——表单。 操作步骤:选择【文件】菜单中的【新建】命令,弹出【新建】对话框,选取【表单】后单击【新建文件】按钮,进入表单设计器,新建一张表单“Form1”,在属性窗口中设置其Caption属性为“求解一元二次方程”,如下图所示。
150
②定义数据环境——本例没有涉及数据库与表操作,此步省略。
③放置控件对象——本例包括标签、文本框和命令按钮对象。 操作步骤:在表单控件工具栏上单击标签按钮,如下图所示,然后将鼠标移到“Form1”表单区域单击,这时就出现一个相应的标签对象,移动标签至合适的位置,并设置其相关属性。其余控件的获取方法一致。
151
各控件的主要属性设置见书上表6.1所示。 表单Form1的设计界面如图所示。
152
④双击【求解】按钮,进入代码编辑窗口,编写Click事件代码如下:
a=val(thisform.text1.value) b=val(thisform.text2.value) c=val(thisform.text3.value) d=b*b-4*a*c if d>=0 x1=(-b+sqrt(d))/(2*a) x2=(-b-sqrt(d))/(2*a) thisform.label6.caption=alltrim(str(x1,5,2)) thisform.label7.caption=alltrim(str(x2,5,2)) else messagebox("该方程没有实根") endif
153
⑤保存表单,并命名。 选择【表单】菜单中的【执行表单】命令,输入a,b,c的值分别为3,-4,1,则运行结果界面如下图所示。
154
6.9 VFP中的类与对象 类就像是一个模板,对象都是由它生成的。类定义了对象的基本属性、事件和方法,从而决定了对象的属性和行为。VFP支持面向对象程序设计方法,提供了丰富的基础类,即VFP基类,用户可以直接使用这些基类来创建自己的子类或对象,从而简化了新类的创建过程,也简化了程序设计。
155
Visual FoxPro的基类 基类是VFP系统内部定义的类,并不存放在某个类库中,在很多情况下要用到VFP所提供的基类。VFP中的基类分成容器类和控件类,相应地,可以分别生成容器对象和控件对象。 容器类可以包含其他对象,并且允许访问这些对象。 例如,表单是一个容器类对象,其中可以加入列表框、编辑框、命令按钮等控件类对象。书上表6.2列出了VFP中常见的容器类及其能够包含的对象。 控件类不能容纳其他对象,没有容器类灵活。 例如:命令按钮是一个控件类对象,在其中不可放入其他对象。由控件类创造的对象,不能单独使用和修改,只能作为容器类中的一个元素,通过由容器类创造的对象修改或使用。书上表6.3列出了VFP中常见的控件类及其功能说明。
156
6.9.2 子类的创建与使用 在VFP中,类的使用是非常灵活的,使用VFP提供的基类就可以完成大多数应用程序的设计。然而,在实际应用中,为了体现使用类的优越性,充分发挥类的功能,在以下情况下,往往需要考虑子类的设计。 封装通用功能。这些功能在应用程序中经常需要用到,而且它们的代码都是一致的,不受具体环境的影响。可以为这些功能定义自己的命令按钮类,在以后的程序设计中,只要创建该类的对象即可,无须再编写代码。 赋予应用程序统一的外观和风格。创建外观独特的表单集类、表单类和控件类,可以使应用程序的所有组件具有相同的风格。
157
VFP子类可以用类设计器创建,也可以用程序代码创建。
在本例中创建关闭按钮类的目的在于保持系统中界面风格的一致,在该类中定义好按钮的标题、字体等属性后,编写Click单击事件代码,在以后的设计中,所有的关闭按钮可以直接由该类创建对象完成,不必每次进行对象属性设置。 1)创建类 选择【文件】菜单中的【新建】命令,在弹出的对话框中选择【类】后,再选择【新建文件】按钮,出现【新建类】对话框如下图所示。
158
在【类名】文本框中,定义类名为“closebutton”,在派生于下拉列表框中,选择基类名或父类名为“CommandButton”,在存储于文本框中,设置可视类库文件名为“closebutton.vcx”。 完成以上操作后,单击【确定】按钮,打开类设计器。类设计器界面与表单设计器界面相似,其容器是类而不是表单。如下图所示。
159
修改按钮类的标题(Caption)属性为“关闭”,设计鼠标单击事件代码(Click Event)为:thisform
修改按钮类的标题(Caption)属性为“关闭”,设计鼠标单击事件代码(Click Event)为:thisform.release,此代码的意义是关闭(释放)这个表单,最后关闭类设计器,Closebutton类就设计好了。 2)使用子类 在表单控件工具栏中单击【查看类】,在弹出的菜单中选择【添加】,如下图所示。
160
在【打开】对话框中选中Closebutton
在【打开】对话框中选中Closebutton.vcx文件,然后单击【确定】按钮,此时控件工具栏上将显示“Closebutton”子类的图标(本例显示命令按钮图标)。这样就可以像使用标准控件一样使用“Closebutton”类了。下图显示了将CloseButton类添加到圆面积计算程序的设计界面。
161
如果希望恢复常用的基类表单控件工具栏,单击【查看类】,在弹出的菜单中选择【常用】即可。
162
6.10 对象的常用属性、事件和方法程序 6.10.1 对象的常用属性
6.10 对象的常用属性、事件和方法程序 对象的常用属性 VFP对象的属性分为3类:数据、布局和其他。一个对象可能有多达六七十种属性。但在程序设计中,并非所有属性都要了解并进行设置。通常一个应用软件,只需要修改对象的最常用的几个属性值即可,其他大部分的属性使用默认值。书上表6.5中给出了对象的常用属性。 属性窗口的操作 对象的某些属性,如长、宽、位置等,用鼠标拖动即可完成设置,当然也可以在属性窗口中设置;而对象的另外一些属性,如标题、颜色、是否可见等,则需借助属性窗口或程序代码完成设置。
163
属性窗口如图所示。 对象列表是一个下拉框,可以单击“▼”弹出一个对象列表框,从中选择一个当前要操作的对象。选项卡将属性进行了分类,默认列出全部属性、事件与方法。属性设置框用于输入或者选择修改属性值。其中: 按钮用于确认对当前属性的修改。 按钮用于取消更改。 按钮用于打开【表达式生成器】。
164
属性列表由2部分构成:名称栏和当前值。当单击一个名称栏时,选中该属性,可以设置它的值。双击名称栏可以打开事件、方法程序的代码编辑窗口。在属性列表中,很容易区分哪些是属性,哪些是事件以及方法程序。
如果名称特性的最后一个单词是Event,说明是一个事件,例如,“Click Event”是单击事件,双击这个名称可以打开代码窗口,编写事件驱动程序。若用户已经为某个事件编写了代码,则当前值显示:“用户自定义过程”。 如果名称栏的最后一个单词不是Event,并且当前值显示“默认过程”,说明是一个方法程序,可以调用它的功能。其他的都是属性,都有一个默认值。在输入属性值时,字符值不需要加定界符,逻辑和日期值要加定界符。
165
对象的常用事件 书上表6.6 给出了VFP中对象的常用事件及触发方式。 说明:焦点(Focus)是指某个时刻,在多个对象中,允许一个选定的对象被操作。对象被选定,它就获得了焦点。焦点的标志可以是文本框内的输入点(竖线光标)、命令按钮的虚线框。焦点一般由用户动作引起,如按 Tab 键或单击控件对象,也可以通过在事件代码中使用 SetFocus 方法程序让某个控件获得焦点。
166
对象的常用方法程序 直接调用的对象方法程序不多,主要有:刷新(Refresh);释放(Release);设置焦点(SetFocus);激活显示表单(Show);隐藏表单(Hide)。 Refresh:重新显示对象,并刷新所有值。可使用Refresh 强制地完全重画表单或控件对象,并更新控件的值。若要在加载另一个表单的同时显示某个表单,或更新控件的内容时,Refresh 很有用。 Release:从内存中释放一个表单或对象的存储空间,常用于关闭一个表单。
167
Setfocus:设置焦点方法是将焦点放到控件上。例如希望启动表单后,直接将焦点设置为第2个命令按钮上,可以在表单的Init事件中输入这样的代码:
thisform.Command2.SetFocus。 Show:显示表单。 Hide:隐藏表单,此时表单并没有从内存中删除。 Show和Hide这2个方法常用于一个表单集的数个表单之间显示和隐藏切换。
168
6.11 面向对象程序的语法基础 6.11.1 在容器分层结构中引用对象
若要处理一个对象,需要知道它相对于容器分层结构的关系。下图列出了一些容器嵌套组织的形式。 在VFP中,对象引用不能直接给出对象的Name属性值,或对象名称,而需要以不同方式说明引用对象的准确“地址”。在容器层次中引用对象就是给VFP提供这个对象的地址。
169
1)绝对引用 是从最顶层容器(表单集或表单)开始,由外到内,分级说明(用圆点分隔对象名称)的一种引用对象的方法。 最高层的容器名称不是其Name属性值,而是其表单或表单集文件名,除非使用 DO FORM 命令中的 NAME 子句改变表单或表单集名称。 例如若要改变表单中一个cmdButton1命令按钮的标题,这个表单保存在 CustView.scx 文件中,可在程序中使用下面的绝对引用语句: CustView.cmdButton1.Caption="Edit" 但不能使用:Form1.cmdButton1.Caption="Edit"
170
2)相对引用 可在表单或表单集中使用关键字 This(对象自身)、Thisform(当前表单)等相对引用对象。 例如,要想在单击命令按钮时改变它的标题,可在命令按钮的 Click 事件代码中包含下面的命令: This.Caption="Edit" 常用的相对引用关键字有: Parent 对象的直接容器 This 对象或对象的过程或事件 Thisform 包含对象的表单 Thisformset 包含对象的表单集
171
对象引用的格式是: 对象地址.对象名.对象的属性、事件或方法程序 对象的地址是该对象在容器分层结构中的位置指示,通常由相对引用关键字和容器名组成。书上表6.7列出了相对引用、绝对引用对象的示例。
172
对象的属性、方法及引用 1)对象的属性及其引用 可以通过以下2种方法设置对象的属性: 在设计阶段利用属性窗口的属性框直接设置对象的属性。 在程序代码中通过赋值实现,其格式为: 对象地址.对象.属性=属性值 例如,给一个表单中的命令按钮(Command1)的Caption属性值赋值为 “确定”,其在程序代码中的书写形式为: Thisform.Command1.Caption="确定"
173
2)对象的方法程序及其引用 对象方法的引用格式为: 对象地址.对象.方法 例如,表单的释放(关闭)需要引用Release方法程序,程序代码为: Thisform.Release
174
3)WITH...ENDWITH 结构 可以使用 WITH...ENDWITH 结构,在代码中同时设置多个属性。 WITH...ENDWITH的语法格式为: WITH对象地址.对象 .属性名=值 ENDWITH
175
例如,要设置表单中表格控件的列的多个属性,可在表单中任何事件或方法程序代码中包含下面的语句:
with Thisform.grdGrid1.grcColumn1 .Width=5 .Resizable=.F. .ForeColor=RGB(0,0,0) .BackColor=RGB(255,255,255) .SelectOnEntry=.T. endwith
176
以上语句等效于: Thisform.GrdGrid1.GrcColumn1.Width=5 Thisform.GrdGrid1.GrcColumn1.Resizable=.F. Thisform.GrdGrid1.GrcColumn1.ForColor=RGB(0,0,0 Thisform.GrdGrid1.GrcColumn1.BackColor=RGB(255,255,255) Thisform.GrdGrid1.GrcColumn1.SelectOnEntry=.T.
177
6.12 表单和表单集 表单和表单集是拥有自己的属性、事件和方法程序的容器类对象。它们均在表单设计器中进行操作和管理。 表单的概念 表单是较高端的一种容器,用于组织和容纳各种控件,以实现一个软件功能的操作界面。表单实际是应用软件的窗口。VFP表单以VFP桌面(Screen屏幕)为基础建立,默认Screen为所有表单的父类。
178
VFP的表单(Form)有3种: 子表单:包含在其他窗口内的子窗口,是VFP默认的表单类型; 浮动表单:类似于子表单,不同点是它可以浮动在Screen或父表单的外面; 顶层表单:与Screen同级,可以在Windows任务栏中极小化,有时作为应用程序的主窗口。
179
作为一种软件界面,表单还有一些主要属性,见书上表6.8所示。
表单以表的格式存储在 .scx的文件中。创建表单时,.scx文件包含了一个表单的记录,一个数据环境的记录和两个内部使用记录。为每个添加到表单或数据环境中的对象添加一个记录。如果创建了表单集,则为表单集及每个新表单各添加了一个记录。每个表单的父容器为表单集,每个控件的父容器为其所在的表单。
180
表单集及其操作 表单集是容纳表单的父容器。表单集包括了2个或多个逻辑关联的表单,可以将它们作为一个整体来操作。例如:如果在表单集中有4个表单,可以在运行时一起装入内存,以后可以由用户操作其中任一个表单,或者用显示/隐藏方法控制它们的操作状态。建立表单集可以方便程序同时控制多个表单,按照程序逻辑打开其中的各个表单。因为表单集中所有表单都是在单个.scx 文件中用一个数据环境定义,所以可自动地同步改变多个表单中的记录指针。运行表单集时(方法与单个表单一样),将加载表单集所有表单和表单的所有对象,可能会多花些时间。
181
表单集一般用于需要同时用多个窗口处理的软件功能,具有以下优点:
能同时显示或隐藏表单集中的全部表单。 能以可视的模式调整多个表单以控制它们的相对位置。 能自动地同步改变多个表单中的记录指针。如果在一个表单的父表中改变记录指针,另一个表单中子表的记录指针将被更新和显示。
182
需要在【表单设计器】中创建表单集。首先新建一个表单,打开【表单设计器】,选择【表单】菜单中的【创建表单集】。在【属性】窗口对象栏中会出现Formset1的表单集对象,它在表单设计器中是不可见的。
创建了表单集以后,可添加新表单或删除表单。选择【表单】菜单中的【添加新表单】,表单设计器中将出现重叠的2个表单:Form1,Form2。设计时需要拖动Form2以便显示Form1。照此方法,可以向表单集Formset1中添加若干表单。若要从表单集中删除表单,先选择要删除的表单,然后选择【表单】菜单中的【移除表单】。 如果表单集中只有一个表单,可删除表单集而只剩下表单。具体方法是选择【表单】菜单中的【移除表单集】。
183
运行表单集的方法是选择【表单】菜单中的【执行表单】。当运行表单集时,若不想在最初让表单集里的所有表单可视的话,可以在表单集运行时,将不希望显示的表单的 Visible 属性设置为“假”(.F.);将希望显示的表单的Visible 属性设置为“真”(.T.)。
184
【例6.34】利用表单集实现2个窗口的切换操作。要求鼠标单击一个窗口,该窗口隐藏,另一个窗口显示。窗口标题要显示该窗口的(Top,Left)属性值坐标。如果活动窗口被移动,隐藏窗口也要随之改变位置,2个窗口的相对位置关系是:Form1.top=Form2.top-10, Form1.left=Form2.left-10。
185
解题步骤如下: ①执行【文件】→【新建】命令新建一个表单,创建表单集,添加一个新表单,将2个表单位置调整如下图所示。
186
②改变2个表单的背景色BackColor。Form1为绿色,Form2为红色。
③打开Form1的Click单击事件代码窗口,输入以下代码: thisformset.form2.caption="form1("+str(this.top+10,3)+","+str(this.left+10,3)+")" thisformset.form2.top=this.top+10 thisformset.form2.left=this.left+10 thisformset.form1.hide thisformset.form2.show
187
④打开Form1的DblClick双击事件代码窗口,输入以下代码:
thisformset.release ⑤打开Form2的Click单击事件代码窗口,输入以下代码:thisformset.form1.caption="form2("+str(this.top- 10,3)+","+str(this.left-10,3)+")" thisformset.form1.top=this.top-10 thisformset.form1.left=this.left-10 thisformset.form2.hide thisformset.form1.show
188
⑥执行【文件】→【保存】命令保存这个表单集。
⑦执行【表单】→【执行表单】命令运行该表单集。 本例程序运行后,首先显示2个表单,鼠标单击其中一个表单,该表单隐藏,另一个表单显示,表单的标题将显示其左上角的坐标。如果拖动表单到新的位置,再单击此表单隐藏,另一表单将在关联位置显示。在Form1表单中双击鼠标结束程序。表单切换的基本思想是表单的隐藏和显示。
189
用户定义属性和方法程序 可以向表单中添加任意多个新的属性和方法程序。属性拥有一个值,而方法程序拥有一个过程代码,当调用方法程序时,即运行这一过程代码。新建的属性和方法程序属于表单或表单集,它们的引用方法与系统提供的标准属性或方法程序一样。 用户定义的属性相当于内存变量,方法程序相当于过程模块。用户定义属性和方法程序扩充了基类的数据个数,增加了程序功能,提高了应用程序的面向对象能力,使得代码和数据的包装更安全。用户定义属性和方法程序的作用范围是整个表单或表单集文件,它们对表单集的所有表单有效,而对于单表单文件,则仅在该表单内有效。
190
用户定义属性和方法程序的具体操作是:首先打开【表单设计器】,选择【表单】中的【新建属性】或【新建方法程序】,如下图所示。
191
在【新建属性】中,输入属性的名称。还可以加入关于这个属性的说明,它将显示在【属性】窗口的底部。默认的属性值为逻辑假,可以根据需要修改自定义属性值,在事件代码中也可以引用和修改其值。
还可以创建数组属性,用VFP数组命令和函数处理它。数组属性包括数组名称及其大小、维数。 例如,要创建一个10行2列的二维数组,其属性名为 arrayprop[10,2]。数组属性在设计期以只读方式显示,只能在运行时刻管理数组,如重新设置数组的维数,也可对数组属性的元素赋值。
192
在【新建方法程序】中,输入方法程序的名称,还可以包含有关这个方法程序的说明,如下图所示。在【属性】窗口的表单对象(如表单名称是Form1)列表框底部,选择新增的方法程序名,双击鼠标,打开过程编辑窗口,输入该方法程序的代码。
193
作为一个例子,下图给出了一个计算圆面积的方法程序代码,其中“parameters r”用于接收圆半径的参数;“return s” 用于返回圆的面积。
194
实际使用自定义方法程序circle_area时,是以函数形式运行,其引用格式为:
nArea=circle_area(nR1) 引用自定义方法程序和调用基类方法程序一样,格式是: ObjectName.MethodName 自定义方法程序接收参数并返回值的格式是: cVariable=ObjectName.MethodName(cParameter, nParameter) 可以在【表单】的【编辑属性/方法程序】中集中管理新建属性和方法程序,也可以移去自定义属性和方法程序。
195
本章小结 VFP同时支持结构化程序设计和面向对象程序设计,它将结构化程序设计和面向对象程序设计结合在一起,帮助用户创建出功能强大、灵活多样的应用程序。 VFP程序由代码组成,代码包括程序控制语句和以命令形式出现的指令、函数或VFP可理解的任何操作。程序由顺序、分支和循环3种基本结构组成。程序可以独立存放成为一个文件,也可以过程文件形式和与之相关的程序存储在一起,程序之间可以相互调用,产生模块化的组织方式。 VFP系统提供的表单设计器、报表设计器、菜单设计器等可视化编程工具,一方面有效地体现了面向对象程序设计的思想,另一方面通过使用这些可视化编程工具,可以大大减轻代码编制过程,避免由于代码编制结果的不可见原因造成程序的反复调试问题,从而简化设计工作。 通过本章的学习,应该掌握程序文件的创建、修改和运行方法;掌握程序的控制结构,过程文件和用户自定义函数的建立与使用;掌握面向对象程序设计基础,这些都将是进一步学习和掌握表单设计、菜单设计和报表设计等方法的基础。
Similar presentations