开发手册 欢迎您!
软件开发者资料库

Pascal - 类

Pascal类 - 从基本概念到高级概念,从简单和简单的步骤学习Pascal,其中包括pascal语法,数据类型,全局和局部变量,单位,函数,循环,常量,结构,数组,枚举,集合,记录,文件,变体记录,指针,链接列表和文本处理。

您已经看到Pascal Objects展示了面向对象范例的一些特征.它们实现了封装,数据隐藏和继承,但它们也有局限性.例如,Pascal Objects不参与多态.因此,类被广泛用于在程序中实现适当的面向对象行为,尤其是基于GUI的软件.

类的定义方式与Object几乎相同,但是指向Object而不是Object本身的指针.从技术上讲,这意味着Class在程序的堆上分配,而Object在堆栈上分配.换句话说,当您将变量声明为对象类型时,它将占用堆栈上与对象大小相同的空间,但是当您声明类类型的变量时,它将始终采用指针的大小在堆栈上.实际的类数据将在堆上.

定义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;

值得注意以下重点和减号;

  • 类定义应仅在程序的类型声明部分下.

  • 使用关键字定义类.

  • 字段是每个类实例中都存在的数据项.

  • 方法在类的定义中声明.

  • 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 constructorprogram 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类的构造函数和析构函数

构造函数是特殊方法,每当创建对象时都会自动调用它们.因此,我们通过构造函数初始化许多东西来充分利用这种行为.

Pascal提供了一个名为create()的特殊函数来定义构造函数.你可以将任意多个参数传递给构造函数.

下面的例子将为一个名为Books的类创建一个构造函数,它将初始化该书的价格和标题.对象创建.

program classExample;{$MODE OBJFPC} //directive to be used for creating classes{$M+} //directive that allows class constructors and destructorstype   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 bookend;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 bookbegin   title := t;end;function Books.getTitle() : String; //retrieves titlebegin   getTitle := title;end;procedure Books.setPrice(p : real); //sets price for a bookbegin   price := p;end;function Books.getPrice() : real; //retrieves pricebegin   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 SchoolPrice: 10Title: Advanced ChemistryPrice: 15Title: AlgebraPrice: 7

与名为create的隐式构造函数一样,还有一个隐式析构函数方法destroy,您可以使用该方法释放该类中使用的所有资源.

继承

Pascal类定义可以选择从父类定义继承.语法如下 :

typechildClas-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 destructorstype   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 bookend;(* 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 bookbegin   title := t;end;function Books.getTitle() : String; //retrieves titlebegin   getTitle := title;end;procedure Books.setPrice(p : real); //sets price for a bookbegin   price := p;end;function Books.getPrice() : real; //retrieves pricebegin   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 bookbegin   author := a;end;function Novels.getAuthor() : String; //retrieves authorbegin   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 WindPrice: 375.99Author: Margaret MitchellTitle: Atlas ShruggedPrice: 467.75Author: 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}用于使用静态成员.