您已经看到Pascal Objects展示了面向对象范例的一些特征.它们实现了封装,数据隐藏和继承,但它们也有局限性.例如,Pascal Objects不参与多态.因此,类被广泛用于在程序中实现适当的面向对象行为,尤其是基于GUI的软件.
类的定义方式与Object几乎相同,但是指向Object而不是Object本身的指针.从技术上讲,这意味着Class在程序的堆上分配,而Object在堆栈上分配.换句话说,当您将变量声明为对象类型时,它将占用堆栈上与对象大小相同的空间,但是当您声明类类型的变量时,它将始终采用指针的大小在堆栈上.实际的类数据将在堆上.
使用类型以与对象相同的方式声明类宣言.类声明的一般形式如下 :
type class-identifier = class private field1 : field-type; field2 : field-type; ... public constructor create(); procedure proc1; function f1(): function-type; end; var classvar : class-identifier;
值得注意以下重点和减号;
类定义应仅在程序的类型声明部分下.
使用类关键字定义类.
字段是每个类实例中都存在的数据项.
方法在类的定义中声明.
Root类中有一个名为 Create 的预定义构造函数.每个抽象类和每个具体类都是Root的后代,所以所有类都至少有一个构造函数.
有一个名为 Destroy的预定义析构函数在Root类中.每个抽象类和每个具体类都是Root的后代,因此,所有类都至少有一个析构函数.
让我们定义一个Rectangle类,有两个整数类型数据成员 - 长度和宽度,一些成员函数用于操作这些数据成员和一个绘制矩形的过程.
type Rectangle = class private length, width: integer; public constructor create(l, w: integer); procedure setlength(l: integer); function getlength(): integer; procedure setwidth(w: integer); function getwidth(): integer; procedure draw; end;
让我们编写一个完整的程序,创建一个矩形类的实例并绘制矩形.这是我们在讨论Pascal Objects时使用的相同示例.您会发现这两个程序几乎相同,但有以下例外和减号;
您需要包含{$ mode objfpc}使用类的指令.
您需要包含{$ m +}指令以使用构造函数.
类实例化与对象实例化不同.只声明变量不会为实例创建空间,您将使用构造函数create来分配内存.
这是完整的示例 :
{$mode objfpc} // directive to be used for defining classes {$m+} // directive to be used for using constructor program exClass; type Rectangle = class private length, width: integer; public constructor create(l, w: integer); procedure setlength(l: integer); function getlength(): integer; procedure setwidth(w: integer); function getwidth(): integer; procedure draw; end; var r1: Rectangle; constructor Rectangle.create(l, w: integer); begin length := l; width := w; end; procedure Rectangle.setlength(l: integer); begin length := l; end; procedure Rectangle.setwidth(w: integer); begin width :=w; end; function Rectangle.getlength(): integer; begin getlength := length; end; function Rectangle.getwidth(): integer; begin getwidth := width; end; procedure Rectangle.draw; var i, j: integer; begin for i:= 1 to length do begin for j:= 1 to width do write(' * '); writeln; end; end; begin r1:= Rectangle.create(3, 7); writeln(' Draw Rectangle: ', r1.getlength(), ' by ' , r1.getwidth()); r1.draw; r1.setlength(4); r1.setwidth(6); writeln(' Draw Rectangle: ', r1.getlength(), ' by ' , r1.getwidth()); r1.draw; end.
编译并执行上述代码时,会产生以下结果 :
Draw Rectangle: 3 by 7 * * * * * * * * * * * * * * * * * * * * * Draw Rectangle: 4 by 6 * * * * * * * * * * * * * * * * * * * * * * * *
可见性表示班级成员的可访问性. Pascal类成员有五种类型的可见性 :
Sr.否 | 可见度&辅助功能 |
---|---|
1 | Public 这些成员始终可以访问. |
2 | 私人 这些只能在包含类定义的模块或单元中访问成员.它们可以从类方法内部或从它们外部访问. |
3 | Private 这些成员只能从类本身的方法中访问.同一单元中的其他类或后代类无法访问它们. |
4 | Protected 这与私有相同,除了这些成员可以访问后代类型,即使它们是在其他模块中实现的. |
5 | Published 这与Public相同,但编译器生成自动流式传输所需的类型信息如果编译器处于{$ M +}状态,则为这些类.已发布部分中定义的字段必须是类类型. |
构造函数是特殊方法,每当创建对象时都会自动调用它们.因此,我们通过构造函数初始化许多东西来充分利用这种行为.
Pascal提供了一个名为create()的特殊函数来定义构造函数.你可以将任意多个参数传递给构造函数.
下面的例子将为一个名为Books的类创建一个构造函数,它将初始化该书的价格和标题.对象创建.
program classExample; {$MODE OBJFPC} //directive to be used for creating classes {$M+} //directive that allows class constructors and destructors type Books = Class private title : String; price: real; public constructor Create(t : String; p: real); //default constructor procedure setTitle(t : String); //sets title for a book function getTitle() : String; //retrieves title procedure setPrice(p : real); //sets price for a book function getPrice() : real; //retrieves price procedure Display(); // display details of a book end; var physics, chemistry, maths: Books; //default constructor constructor Books.Create(t : String; p: real); begin title := t; price := p; end; procedure Books.setTitle(t : String); //sets title for a book begin title := t; end; function Books.getTitle() : String; //retrieves title begin getTitle := title; end; procedure Books.setPrice(p : real); //sets price for a book begin price := p; end; function Books.getPrice() : real; //retrieves price begin getPrice:= price; end; procedure Books.Display(); begin writeln('Title: ', title); writeln('Price: ', price:5:2); end; begin physics := Books.Create('Physics for High School', 10); chemistry := Books.Create('Advanced Chemistry', 15); maths := Books.Create('Algebra', 7); physics.Display; chemistry.Display; maths.Display; end.
编译并执行上述代码时,会产生以下结果 :
Title: Physics for High School Price: 10 Title: Advanced Chemistry Price: 15 Title: Algebra Price: 7
与名为create的隐式构造函数一样,还有一个隐式析构函数方法destroy,您可以使用该方法释放该类中使用的所有资源.
Pascal类定义可以选择从父类定义继承.语法如下 :
type childClas-identifier = class(baseClass-identifier) < members > end;
以下示例提供了一个小说类,它继承了Books类并根据需求添加了更多功能.
program inheritanceExample; {$MODE OBJFPC} //directive to be used for creating classes {$M+} //directive that allows class constructors and destructors type Books = Class protected title : String; price: real; public constructor Create(t : String; p: real); //default constructor procedure setTitle(t : String); //sets title for a book function getTitle() : String; //retrieves title procedure setPrice(p : real); //sets price for a book function getPrice() : real; //retrieves price procedure Display(); virtual; // display details of a book end; (* Creating a derived class *) type Novels = Class(Books) private author: String; public constructor Create(t: String); overload; constructor Create(a: String; t: String; p: real); overload; procedure setAuthor(a: String); // sets author for a book function getAuthor(): String; // retrieves author name procedure Display(); override; end; var n1, n2: Novels; //default constructor constructor Books.Create(t : String; p: real); begin title := t; price := p; end; procedure Books.setTitle(t : String); //sets title for a book begin title := t; end; function Books.getTitle() : String; //retrieves title begin getTitle := title; end; procedure Books.setPrice(p : real); //sets price for a book begin price := p; end; function Books.getPrice() : real; //retrieves price begin getPrice:= price; end; procedure Books.Display(); begin writeln('Title: ', title); writeln('Price: ', price); end; (* Now the derived class methods *) constructor Novels.Create(t: String); begin inherited Create(t, 0.0); author:= ' '; end; constructor Novels.Create(a: String; t: String; p: real); begin inherited Create(t, p); author:= a; end; procedure Novels.setAuthor(a : String); //sets author for a book begin author := a; end; function Novels.getAuthor() : String; //retrieves author begin getAuthor := author; end; procedure Novels.Display(); begin writeln('Title: ', title); writeln('Price: ', price:5:2); writeln('Author: ', author); end; begin n1 := Novels.Create('Gone with the Wind'); n2 := Novels.Create('Ayn Rand','Atlas Shrugged', 467.75); n1.setAuthor('Margaret Mitchell'); n1.setPrice(375.99); n1.Display; n2.Display; end.
编译并执行上述代码时,会产生以下结果 :
Title: Gone with the Wind Price: 375.99 Author: Margaret Mitchell Title: Atlas Shrugged Price: 467.75 Author: Ayn Rand
值得注意以下重点和减号;
Books类的成员具有受保护的可见性.
Novels类有两个构造函数,因此 overload 运算符用于函数重载.
Books.Display过程已被声明为虚拟,因此Novels类中的相同方法可以覆盖它.
Novels.Create构造函数使用继承的关键字调用基类构造函数.
定义接口以向实施者提供通用功能名称.不同的实现者可以根据需要实现这些接口.你可以说,接口是由开发人员实现的骨架.以下是接口的示例 :
type Mail = Interface Procedure SendMail; Procedure GetMail; end; Report = Class(TInterfacedObject, Mail) Procedure SendMail; Procedure GetMail; end;
请注意,当一个类实现一个接口时,它应该实现该接口的所有方法.如果未实现接口的方法,则编译器将给出错误.
抽象类是不能的类实例化,只继承.通过在类定义中包含单词symbol abstract来指定抽象类,如此 :
type Shape = ABSTRACT CLASS (Root) Procedure draw; ABSTRACT; ... end;
从抽象类继承时,父类声明中标记为abstract的所有方法都必须由子类定义;此外,必须使用相同的可见性定义这些方法.
将类成员或方法声明为静态可使它们无需实例化即可访问班上的.声明为static的成员无法使用实例化的类对象访问(尽管静态方法可以).以下示例说明了概念 :
program StaticExample; {$mode objfpc} {$static on} type myclass=class num : integer;static; end; var n1, n2 : myclass; begin n1:= myclass.create; n2:= myclass.create; n1.num := 12; writeln(n2.num); n2.num := 31; writeln(n1.num); writeln(myclass.num); myclass.num := myclass.num + 20; writeln(n1.num); writeln(n2.num); end.
编译并执行上述代码时,会产生以下结果 :
12 31 31 51 51
您必须使用指令{ $ static on}用于使用静态成员.