1、第5章 Object Pascal程序设计语言5.1 5.1 常量与变量常量与变量5.2 5.2 数据类型数据类型5.3 5.3 运算符运算符5.4 5.4 语句语句5.5 5.5 过程与函数过程与函数5.6 5.6 程序结构程序结构5.7 5.7 面向对象程序设计理论面向对象程序设计理论5.1 常量与变量5.1.1 声明变量5.1.2 声明常量返回本章首页5.1.1 声明变量变量是程序代码中代表一个内存地址的标识符,而此地址的内存内容在程序代码执行时可以被改变。在所有变量说明以前加上保留字Var。变量说明左边是变量的名称,右边则是该变量的类型,中间用冒号隔开。另外,在声明变量时注意:一是变量
2、名要符合Pascal语言关于标识符的规则;二是必须指定变量的类型,三是尽量使定义的变量或标识符有意义,以便于记忆。返回本节返回本节5.1.2 声明常量1纯常量:是一种预先定义的标识符,其值在定义后永远不会改变。5.类型常量:用于保存数组、记录、过程以及指针等类型的值。类型常量不能出现在常量表达式中。声明类型常量的语法规则为:Const Identifer:Type=Value返回本节返回本节5.2 数据类型5.2.1 简单类型5.2.2 字符串类型5.2.3 结构数据类型5.2.4 指针类型返回本章首页5.2.1 简单类型1有序类型(1)整数类型(2)字符类型(3)布尔类型(4)枚举型(5)子
3、界类型5.实数类型 包括Real48、Single、Double、Extended、Comp和Currency。通常使用的Real和Double类型是等价的。返回本节返回本节5.2.2 字符串类型Delphi主要支持两种类型的字符串:ShortString和AnsiString。ShortString数据类型主要是为了能和Delphi1.0和Borland Pascal的早期版本兼容。String既可以是ShorString类型也可以是AnsiString类型,缺省是AnsiString类型。返回本节返回本节5.2.3 结构数据类型 1、集合类型 2、数组类型 3、记录类型 4、文件类型 5、
4、类类型 6、类引用类型 7、接口类型等返回本节返回本节5.2.4 指针类型在Delphi中创建一个数据结构时,首先要分配内存空间。分配的内存空间用于存储数据结构中的数据。使用指针可以使程序不必每次需要时都去分配,只要申请一次,其他过程或函数使用同一块内存空间时,只需使用该内存空间的地址即可。在运算上,指针变量与一般变量无区别,如p:=q;。当要引用指针所指向的变量时,必须在指针后使用“”。返回本节返回本节5.3 运算符5.3.1 算术运算符5.3.2 逻辑运算符5.3.3 关系运算符5.3.4 运算符的优先级返回本章首页5.3.1 算术运算符算术运算符用于完成二元或一元算术运算。(见表表5-1
5、和和表5-2)运算符运算符功能功能运算数类型运算数类型运算结果类型运算结果类型+加法运算加法运算IntegerRealIntegerReal-减法运算减法运算IntegerRealIntegerReal*乘法运算乘法运算IntegerRealIntegerReal/除法运算除法运算IntegerRealRealRealDiv整型除法整型除法IntegerIntegerMod计算余数(取模)计算余数(取模)IntegerInteger表5-1 二元运算符表表5-2 一元运算符一元运算符运算符运算符功能功能运算数类型运算数类型运算结果类运算结果类型型+同号同号IntegerRealIntegerR
6、eal-取负值取负值IntegerRealIntegerReal返回本节返回本节5.3.2 逻辑运算符逻辑运算符实际上可分为两类:逻辑运算符逻辑运算符实际上可分为两类:逻辑运算符和布尔运算符。(见和布尔运算符。(见表表5-3 和和表表5-4)运算符运算符功能功能运算数类型运算数类型运算结果类型运算结果类型Not按位按位非非IntegerBooleanAnd按位按位与与IntegerBooleanOr按位按位或或IntegerBooleanXor按位按位异或异或IntegerBooleanShl按位左移按位左移IntegerBooleanShr按位右移按位右移IntegerBoolean表表5-
7、3 逻辑运算符逻辑运算符表表5-4 布尔运算符布尔运算符 运算符运算符功能功能运算数类型运算数类型运算结果类型运算结果类型Not真假值反向真假值反向BooleanBooleanAnd逻辑逻辑and运算运算BooleanBooleanOr逻辑逻辑or运算运算BooleanBooleanXor逻辑逻辑xor运算运算BooleanBoolean返回本节返回本节5.3.3 关系运算符表5-5 关系运算符(1)运算符运算符功能功能运算数类型运算数类型运算结果类运算结果类型型=等于等于兼容的简单类型、类类型、集合类型、兼容的简单类型、类类型、集合类型、字符串类型字符串类型Boolean不等于不等于兼容的简
8、单类型、类类型、集合类型、兼容的简单类型、类类型、集合类型、字符串类型字符串类型Boolean大于大于兼容的简单类型、字符串类型兼容的简单类型、字符串类型Boolean=大于或大于或等于等于兼容的简单类型、字符串类型兼容的简单类型、字符串类型Boolean=包含包含兼容的集合类型兼容的集合类型BooleanIn属于属于兼容的集合类型兼容的集合类型Boolean表5-5 关系运算符(2)返回本节返回本节5.3.4 运算符的优先级表5-6 运算符的优先级运算符运算符优先级优先级分类分类Not1(最高)(最高)一元运算符一元运算符*,Div,Mod,Shl,Shr,as2乘法运算符乘法运算符,Or,
9、Xor3加法运算符加法运算符=,=,In4关系运算符关系运算符返回本节返回本节5.4 语句5.4.1 赋值语句5.4.2 块语句5.4.3 控制语句返回本章首页5.4.1 赋值语句下面的例子就是一个最简单的赋值语句:X:=Y+Z;在赋值语句的左边是此赋值的数据变量,右边是一个数值或者表达式,中间用:=连接。返回本节返回本节5.4.2 块语句块语句是一种把若干简单语句组合成复杂语句的方式。经常使用的块语句是BeginEnd语句,下面的例子就是一个简单的块语句:Begin A:=5;C:=A*8/2;End;返回本节返回本节5.4.3 控制语句Object Pascal使用控制语句来控制程序的执行
10、顺序。7个是分支语句,3个是循环语句:(1)分支语句 If语句、Case语句、GOTO语句、Break语句、Continue语句、Exit语句、Halt语句。(2)循环语句 Repeat语句、While语句、For语句返回本节返回本节条件选择结构(一)if J 0 thenbegin Result:=I/J;Count:=Count+1;endelse if Count=Last then Done:=Trueelse Exit;分支语句条件选择结构(二)case selectorExpression ofcaseList1:statement1;.caseListn:statementn;e
11、lsestatements;end;分支语句两种选择结构比较两种选择结构比较case I of 1.5:Caption:=Low;6.9:Caption:=High;0,10.99:Caption:=Out of range;else Caption:=;end;if I in 1.5 then Caption:=Low else if I in 6.10 then Caption:=High else if(I=0)or(I in 10.99)then Caption:=Out of range else Caption:=;分支语句循环结构(一)while I 0 dobegin if O
12、dd(I)then Z:=Z*X;I:=I div 2;X:=Sqr(X);end;循环语句循环结构(二)for I:=1 to 10 doBegin AI:=I;end;for I:=10 downto 1 doBegin AI:=I;end;循环语句循环结构(三)repeat K:=I mod J;I:=J;J:=K;until J=0;循环语句循环结构控制语句BreakContinue循环语句5.5 过程与函数5.5.1 过程与函数的定义和调用5.5.2 过程与函数的参数返回本章首页5.5.1 过程与函数的定义和调用1过程与函数的定义 每一个过程或函数都从表头开始,表头包含其识别名称及外
13、部传入的参数,过程开始处为保留字Procedure,如:Procedure Name(Const Peter:Integer);F u n c t i o n N a m e(C o n s t P e t e r:Integer):integer;函数与过程不同的地方是,函数有返回值,这样在函数的首部就多了一个返回结果类型。5.过程与函数的调用调用过程的语句只是一个独立的语句,而调用函数的语句不是一个独立的语句,它必须要返回一个值,且返回数值数据类型必须与定义相同。例如:MinNumber:=Min(People);另外也可以将函数调用直接作为操作数。例如:X:=Min(People)+50
14、;5.指示字(1)External:表示过程或函数是外部的,通常用于从动态连接库中引用过程或函数。(2)Assembler:表示过程或函数是使用嵌入式汇编语言编写的。(3)Forward:表示过程或函数是向前查找的。该过程或函数的定义必须在后面的某个地方定义。4与调用约定在声明函数或过程时,可以在附属块中指定函数过程或函数的参数传递方式。主要有Register,Pascal,Cdecl,Stdcall,Safecall等五种传递方式。缺省的调用方式是Register。返回本节返回本节5.5.2 过程与函数的参数 经常用到的参数主要有以下几种:1传值参数传值参数会把外部传入的值作一份拷贝,保存在
15、此参数内,因此在过程内对此参数所作的任何改变,都不会影响外部传入的值。参数前不加任何的保留字,即为传值参数。5.引用参数通过引用来传递参数时,允许过程或函数改变参数的值。通过引用传递的参数由关键字Var表示。5.常量参数在过程或函数的运行当中,如果形参的值不变,就可以使用常量来传递参数。与引用传递的参数不同的是,将参数前加上的保留字Var改为Const。返回本节返回本节结束放映返回本章首页5.6 程序结构5.6.1 程序模块结构5.6.2 库单元(unit)结构procedure TForm.Button1Click(Sender procedure TForm.Button1Click(Se
16、nder TobjectTobject););varvar 程序模块的说明部分程序模块的说明部分 Name:string;Name:string;begin begin 程序模块的语句部分程序模块的语句部分 Name:=Edit1.Text;Name:=Edit1.Text;Edit5.Text:=Welcome to Delphi+Name;Edit5.Text:=Welcome to Delphi+Name;end;end;程序模块结束程序模块结束 5.6.1 程序模块结构5.6.2 库单元(unit)结构unit interfaceuses 公有说明implementationuses
17、私有说明过程和函数的执行部分 initialization 选择性的选择性的初始化程序end.5.7 面向对象程序设计理论5.7.1 面向对象的程序设计方法基础5.7.2 对象和类5.7.1 面向对象的程序设计方法基础面向对象的程序设计方法(Object Oriented Programming)简称为OOP,它的基本元素是类(Class)和对象(Object)。使用对象模拟自然的或抽象的实体,对用户掩盖了实现的复杂性(封装),并且能使程序员付出尽可能小的代价,获得尽可能大收益(继承),提高软件的开发效率。5.7.2 对象和类类是对事物的特性及操作的抽象描述,对象是类的实体。从C语言的角度来看
18、,类是结构的自然演化,类就是包含数据声明和函数声明的结构。类既包含了数据又包含了代码,对类中成员的存取是受控制的,这一控制不仅针对数据,也针对代码,类的这个特性被称为封装。封装的说明计算过程的演变:手工计算全部手工和脑力完成计算器(面向过程)部分借助非人力因素,但仍需对过程进行控制和管理计算机(面向对象)完全借助非人力因素,运算一旦开始,可以无需人工的介入类的说明The person watching TV does not need to know the TV sets structure and how it works inside.But he needs to control th
19、e TV set.TV needs to provide an interface.Internal State对象的例子对象的例子:TV开请求开请求变更频道变更频道调节声音调节声音Turn OnTurn OffChange ChannelAdjust VolumeOthers.被封装的行为和状态被封装的行为和状态关请求关请求类的说明特性:脚个数物理位置长度宽度高度形状操作:搬动改造“桌子”类定义类(最简单实例)关键字class引导类声明Type是类型声明符类体类定义结束TypeTdate=class Month,Day,Year:Integer Procedure SetValue(m,d,
20、y:Integer);Function LeapYear:Boolean;End;定义类(最简单实例)类函数定义前缀Procedure Tdate.SetValue(m,d,y:Integer);Begin Month:=m;Day:=d;Year:=y;End;Function Tdate.LeapYear:Boolean;Begin if(Year mod d0)then LeapYear:=false else if(Year mod 1000)then Result:=True else if(Year mod 4000)then LeapYear:=false else Result
21、:=True;End;使用类(最简单实例)Var Aday1,Aday2:Tdate;Begin/CreateAday1:=Tdate.Create;Aday2:=Tdate.Create;/useAday1.SetValue(1,1,2000);if Aday1.Leapyear thenShowmessage(Leap year:+IntToStr(Aday1.Year);/destroyAday1.Free;End;类是一个模板,就象数据类型一样,使用的时候必须定义类变量。定义类变量称为类的实例化,类的实例化变量就是对象。对象是具有某一个类所描述的共性的,但又有各自特性的个体。类的变量声
22、明,只保存对象的引用即指针,对象实际的内存需要类中的创建函数Create创建。类存取控制类的成员的使用者有三类:1.类本身5.一般用户5.派生类每种使用者权限不同,对应与不同的使用权限,有不同的关键字说明。类私有成员(private)类本身(或类的友元)可以存取类的私有成员。派生类不能存取其父类的私有成员。因为类必须在程序中使用,所以类必须至少有一个非私有的成员。类成员的存取控制缺省为私有类型。类私有成员(实例)Type Tdate=class Private Month,Day,Year:Integer Public Procedure SetValue(m,d,y:Integer);Fun
23、ction LeapYear:Boolean;Function GetText:String;Procedure Increase;End;类公有成员(public)在程序中使用给定类的对象,必须能存取成员数据、成员函数或两者。为使这些成员在程序中可以被存取,必须在类中声明公有部分。在公有部分定义的内容允许被其他对象无限制地存取。通常可以有控制地使用公有成员存取私有数据,调用私有成员函数完成工作。类公有成员(实例)TypeTdate=class Private Month,Day,Year:Integer Public Procedure SetValue(m,d,y:Integer);Fun
24、ction LeapYear:Boolean;Function GetText:String;Procedure Increase;End;类保护成员(protected)用于部分地保护对象方法和数据。保护元素只能被当前类和它的子类所访问。与私有成员主要的区别是:当父类与子类不在同一代码单元中时,私有成员只能为当前类使用,即派生类不能存取父类的私有成员。类保护成员(实例)Type A=Classprivate value_1:Integer;protected value_2:Integer;public value_3:Integer;end;B=Class(A)public value_4
25、:Integer;Procedure FB();End;问:在类B的FB函数中可以直接存取的成员有哪些?问:通过类B的对象在程序中可以直接访问的成员有哪些?特殊类成员1、构造函数(constructor)2、析构函数(destructor)构造函数构造函数是用来建立某给定类的对象的。一个类可以不定义构造函数,也可以定义一个或多个构造函数。编译系统保证类的构造函数先于其他函数被调用。构造函数(续一)构造函数可以使用任何名称,但即使我们不使用Create作为名称,基类Tobject的Create仍可使用。构造函数除为对象分配内存外,还可以进行对象的初始化工作。一个类的构造函数可以有多个,但必须采用
26、不同的参数(即重载)。重载在OOP中,简洁性是很重要的。用户使用系统时对系统了解得越少越好,调用函数时需要的细节越少越好(参数的个数和类型)。如果函数能够接受各种类型的参数,由编译器区分细节,则会很方便。重载(续)所谓重载就是同一个符号在不同的上下文中代表不同的含义。其关键字为overload函数重载非成员函数重载成员函数重载重载(例)Constructor Create;overload;Constructor Create(y,m,d:Integer);overload;procedure setvalue(y,m,d:integer);overload;procedure setvalu
27、e(NewDate:TDateTime);overload;构造函数小结缺省构造函数有特定初始化值的构造函数构造函数没有返回类型同一个类可以有多个构造函数析构函数析构函数在对象不再被使用时,执行一些必须的操作,一般是构造函数的逆操作。一个类只能声明一个析构函数。析构函数是构造函数的逆函数。析构函数(续)1、析构函数没有返回类型析构函数的命名是在类名前加destructor一个类只能有一个析构函数析构函数没有参数例:例:Destructor Destroy;三、封装与窗体封装的一个主要思想是减少程序内全局变量的使用数目。全局变量的变化会影响所有程序。而类内元素的变化只影响类内程序。当需改变类中某
28、元素的值时,只需改变该类的一些对象方法的代码,就不用再做其它的事了。可以说信息隐含与封装改变相关。封装与窗体(实例1)当程序具有多个相同类型的窗体时,为了使部分数据每个窗体都能使用,可以采用两种方法:全局变量,类内变量。其结果有很大区别。实例见程序例子test2mdi01MDIAPP.DPR封装与窗体(实例2)当定义类的实例时,封装在类内的变量归属于类的每个实例。实例见程序例子test2Tmydate02project1.DPR封装与窗体(实例3)self关键字是Object Pascal中每个对象方法的一个隐含参数,主要出现在区分对象方法于过程或函数(与类无关)的部分。当声明同类的多个对象时
29、,就需要self,这样每当向一个对象应用对象方法时,对象方法就只在自己的数据上进行操作,而不会影响到其它兄弟对象。实例见程序见:例子test2Tmydate02和例子test2DyButton四、继承性从编码角度讲,从基类中派生类以较低代价换来了大量的灵活性。一旦产生了可靠的基类,只需要调试派生类中所做的修改即可。派生类从它的父类中继承性质时,可使派生类对它们进行扩展、限制、改变、删除或不做任何修改。所有这些变化归结成两类基本的面向对象技术:性质约束和性质扩展。性质扩展使用派生类的主要原因是基类提供了派生类需要的部分特征。常常基类很类似于需要的类,但不完全相同,其中有的函数需要稍微扩展一下。如
30、果在派生类中再次书写整个函数会浪费很多时间。因此,可以充分利用类的继承特性,将函数的功能进行扩充。性质约束用户常常找到一些基本合乎要求的类,但是它们可能会有一些不受欢迎的行为。这时,可以使用性质约束的方法来使派生类不具有某种功能。单一继承(例)派生类由一个父类产生。Type Tform1=class(Tform)End;Type TNewDate=class(TMydate)public function GetText:string;End;实例见:继承/TNewDate基类存取限定符private:派生类不能存取(在不同代码单元)。Prodetected:基类中所有prodetected成
31、员在派生类中变为私有成员。public:基类中所有public成员在派生类中仍为public成员,基类存取权限变更 派生类可以对基类中所有成员的存取权限进行重新定义。构造函数的调用类实例化时,需要调用其构造函数。如果该类是派生类,必须调用其父类的构造函数,如果其父类也是派生出来的,重复该过程直至到达非派生的基类。析构函数的调用析构函数的调用顺序与构造函数的调用顺序相反。继承与类型的兼容性数值类型的兼容性:只有当两个值的数据类型(类型名称)相同时,才能视为相同。相似类型间可以通过类型强制转换使之兼容。pchar(st)类的兼容性:当派生类向基类或父类赋值时,视为兼容。反之,则不然。实例参见:继承
32、/TAnimal可视化窗体继承程序员可以简单地从已有的窗体继承一个窗体,并添加新组件或改变已有窗体的属性。注意:永远不要改动组件名称,不管是在父窗体内还是在子窗体内;在编辑一个窗体时,要一直打开其父窗体。实例参见:继承/窗体1多重继承Delphi原则上不支持多重继承,但可以通过接口方式实现此目的。此处所谓接口即类的纯抽象声明,其关键字为Interface。Type ICanFly=Interface 751AE4D8-669F-4801-9335-F1B9567B2561 function Fly:string;End;多重继承的实现一个与动物有关的类层次结构,可以按标准(哺乳动物、鸟类、昆虫
33、等)或按特点(飞行、行走、游泳等)分类。Tobject TInterfacedObject Tanimal TMammal Tbat TMonkey Tbird Teagle Tpenguin TduckIUnknown IAnimal IMammal IBird ICanFly ICanWalk ICanSwim实例参见:继承/Tanimal all五、多态与覆盖多态的基本思想:用户调用一个对象方法,并将其应用于一个变量,但具体调用哪一个方法将依赖与变量相关的对象类型,由于类型的可兼容性原则,Delphi在运行前不能确定与变量相关对象的实际类。即一个给定的对象方法可能有多种版本,一次单独的方
34、法调用可能是这些版本中的任何一种。多态的定义多态的定义在父类中以virtual描述,派生类中以override与之对应。Tanimal=ClassPublic function Voice:string;virtual;End;TDog=Class(Tanimal)Public function Voice:string;override;End;覆盖派生类中可以覆盖父类中的静态的和虚拟的方法。覆盖静态的只需在派生类中添加该方法即可,其参数可以与父类不同。覆盖虚拟的方法,必须指定相同的参数并使用关键字override。TMyClass=ClassPublic procedure One;vir
35、tual;procedure Two;Static MethodEnd;TMySubClass=Class(TMyClass)Public procedure One;override;procedure Two;Static MethodEnd;覆盖(续一)覆盖对象方法有两种典型方法。一种是用新版本替代老版本,另一种是添加新代码,此时应使用关键字inherited。Procedure TMySubClass.One;Begin /New Code /Call inherited procedure from Tmyclass.One inherited One;End;抽象的方法关键字abs
36、tract只声明在当前类的子类中定义的方法,当前类并不对此方法进行定义。其使用方式类似于C语言中的函数声明。实例参见:多态/TAnimal2多态的窗体如果向窗体添加一个事件处理程序,然后在一个继承窗体内改变它。实例参见:多态/窗体2六、高级编程技术类对象方法与类数据对象方法指针类引用Published标示符类中属性事件创建自己的组件类对象方法与类数据类方法只与类有关,与类的对象或对象实例无关。类数据为类中静态数据,定义在类所在单元的implementation中的变量。Type Myclass=class class function GetTotal:string;实例参见:实例参见:例子例
37、子高级编程高级编程带有对象计数器的类带有对象计数器的类对象方法指针对象方法的指针类型与过程类型相象,只不过对象方法指针引用的是对象方法。Type IntPrdType=Procedure(Num:Integer);IntMethodPtType=procedure(Num:Integer)of object;实例参见:实例参见:例子例子高级编程高级编程更新的计数器更新的计数器Anotherclass=class x:integer;procedure Add(N:integer);End;MyClass=class value:integer;Operation:IntMethodPtType
38、;End;Var Myobject:Myclass;AnotherObject:AnotherClassMyobject.Operation:=AnotherObject.Add类引用类的引用不是类,它不是对象,也不是对象的引用,它只是对类类型的引用。Type TMyClassRef=class of Tmyclass;实例参见:实例参见:例子例子高级编程高级编程运行时创建组件运行时创建组件Var AClassRef:TmyclassRef;AnObject:Tmyclass;BeginAClassRef:=Tmyclass;AnObject:=AClassRef.create;Var ACl
39、assRef:TmyclassRef;AnObject:Tmyclass;Begin AClassRef:=Tmyclass;AnObject:=Tmyclass.create;Published标示符Published元素或对象方法,不但能在运行时而且能在设计时使用。通常,类中缺省部分为Published元素。在窗口类中,类声明与Private之间为窗体声明中初始Published部分。只有在此部分中的组件和对象方法,才能在Object Inspector中显示。属性属性定义了对象状态和行为的特征。属性是读写对象方法的名称或直接访问数据的名称。换句话说,用户读一个属性值或改动它,就可能正在访
40、问一个元素(甚至是私有的)或调用一个对象方法。实例参见:实例参见:例子例子高级编程高级编程属性属性事件从技术上讲,大多数事件是在收到相应的Windows消息后被触发的,尽管事件与消息不是一一对应的。从理论上看,事件是向窗口发送消息的结果,并且该窗口可以响应该消息。实际上,对于类或组件来说,事件就是属性,是类或组件响应系统操作的接口。要处理组件的时间,可以赋予对象方法相应的事件属性。实例参见:实例参见:例子例子高级编程高级编程事件事件创建自己的组件自定义组件必须由Tcomponent类或其子类中继承。加入一个新的构造函数如下。并添加一个注册函数Register。实例参见:实例参见:例子例子高级编程高级编程自定义组件自定义组件Constructor Create(Aowner:Tcomponent);overload;override;Constructor Create(y,m,d:integer);reintroduce;override;Procedure Register;Begin RegisterComponents(Md4,Tdate);End;