类
类被广泛用于该程序中实现适当的针对对象行为,尤其是基于GUI的软件。
类的定义方式与对象几乎相同,但是指向对象而不是对象本身的指针。从技术上讲,这意味着类在程序的堆上分配,而对象在上面上分配。换句话说,当您将变量声明为对象类型时,占用占用的体积与对象大小相同的空间,但是当您声明类型时,始终将指针采用指针的大小在交替上。实际的类数据将在堆上。
定义Pascal类
使用类型以与对象相同的方式声明类宣言。类声明的一般形式如下:
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;
类定义应仅在程序的类型声明部分下。
使用类关键字定义类。
一级是每个类实例中都存在的数据项。
方法在类的定义中声明。
根类中有一个名为创建的预定义构造函数。每个抽象类和每个特定类都是根的后代,所以所有类都至少有一个构造函数。
有一个称为Destroy的预定义析构函数在根类中。每个抽象类和每个特定类都是根的后代,因此,所有类都至少有一个析构函数。
让我们定义一个矩形类,有两个整数类型数据成员-长度和宽度,一些成员函数操作这些数据成员和一个矩形的过程。
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} // 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类成员有五种类型的可见性:
序号 |
可见度 |
辅助功能 |
1 |
公开 |
这些成员始终可以访问。 |
2 |
私人 |
这些只能在包含类定义的模块或单元中访问成员。它们可以从类方法内部或从其外部访问。 |
3 |
私人的 |
这些成员只能从类本身的方法中访问。同一单元中的其他类或后代类无法访问它们。 |
4 |
受保护的 |
这与私有相同,除了这些成员可以访问后代类型,甚至它们是在其他模块中实现的。 |
5 |
已发表 |
这与公开相同,但编译器生成自动流式传输所需的类型信息如果编译器处于{$ M +}状态,则为这些类。已发布部分中定义的分段必须是类类型。 |
Pascal类的构造函数和析构函数
构造函数是特殊方法,每当创建对象时都会自动调用它们。因此,我们通过构造函数初始化许多东西来充分利用这种行为。
Pascal提供了一个名为create()的特殊函数来定义构造函数。您可以将任意多个参数传递给构造函数。
下面的例子将为一个称为书籍的类创建一个构造函数,初始化该书的价格和标题。
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
与所谓创建的隐式构造函数一样,还有一个隐式析构函数方法破坏,您可以使用该方法释放该类中使用的所有资源。
从
Pascal类定义可以选择从父类定义继承。语法如下:
type
childClas-identifier = class(baseClass-identifier)
< members >
end;
以下示例提供了一个小说类,它继承了书籍类并根据需求添加了更多功能。
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
图书类的成员具有受保护的可见性。
类有两个构造函数,因此过载运算符为函数重载。
接口
定义接口以向实施者提供通用功能名称。不同的实现者可以根据需要实现这些接口。你可以说,接口是由开发人员实现的框架。以下是接口的示例:
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;
从抽象类继承时,父类声明中标记为抽象的所有方法都必须由子类定义;且,必须使用相同的可见性定义这些方法。
静态关键字
将类成员或方法声明为静态可以对其进行补充的实例化可以直接访问班上的。声明为静态的成员无法使用实例化的类对象访问(尽管静态方法可以)。
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}用于使用静态成员。