Pascal - 面向物件



我們可以想象我們的宇宙是由不同的物體組成的,例如太陽、地球、月球等。類似地,我們可以想象我們的汽車是由不同的物體組成的,例如車輪、方向盤、齒輪等。同樣,存在面向物件的程式設計概念,它假設一切都是物件,並使用不同的物件來實現軟體。在 Pascal 中,有兩種結構化資料型別用於實現現實世界中的物件:

  • 物件型別
  • 類型別

面向物件的概念

在深入瞭解之前,讓我們定義與面向物件 Pascal 相關的重要的 Pascal 術語。

  • 物件 - 物件是一種特殊的記錄,它包含類似於記錄的欄位;但是,與記錄不同,物件包含過程和函式作為物件的一部分。這些過程和函式作為指向與物件型別關聯的方法的指標儲存。

  • - 類的定義方式幾乎與物件相同,但建立方式有所不同。類分配在程式的堆上,而物件分配在棧上。它是指向物件的指標,而不是物件本身。

  • 類的例項化 - 例項化意味著建立該類型別的變數。由於類只是一個指標,因此當宣告類型別的變數時,僅為指標分配記憶體,而不是為整個物件分配記憶體。只有當它使用其建構函式之一進行例項化時,才會為物件分配記憶體。類的例項也稱為“物件”,但不要將其與 Object Pascal 物件混淆。在本教程中,我們將使用“Object”表示 Pascal 物件,使用“object”表示概念物件或類例項。

  • 成員變數 - 這些是在類或物件內部定義的變數。

  • 成員函式 - 這些是在類或物件內部定義的函式或過程,用於訪問物件資料。

  • 成員的可見性 - 物件或類的成員也稱為欄位。這些欄位具有不同的可見性。可見性指的是成員的可訪問性,即這些成員在何處可訪問。物件具有三個可見性級別:public、private 和 protected。類有五種可見性型別:public、private、strictly private、protected 和 published。我們將詳細討論可見性。

  • 繼承 - 當透過繼承父類的現有功能來定義類時,則稱其為繼承。此處子類將繼承父類所有或部分成員函式和變數。物件也可以被繼承。

  • 父類 - 被另一個類繼承的類。這也被稱為基類或超類。

  • 子類 - 從另一個類繼承的類。這也被稱為子類或派生類。

  • 多型性 - 這是面向物件的概念,其中同一個函式可以用於不同的目的。例如,函式名稱將保持不變,但它可以接受不同數量的引數,並且可以執行不同的任務。Pascal 類實現多型性。物件不實現多型性。

  • 過載 - 它是多型性的一種型別,其中一些或所有運算子根據其引數的型別具有不同的實現。類似地,函式也可以過載,具有不同的實現。Pascal 類實現過載,但物件不實現。

  • 資料抽象 - 任何表示資料的形式,其中實現細節被隱藏(抽象)。

  • 封裝 - 指將所有資料和成員函式封裝在一起以形成物件的概念。

  • 建構函式 - 指一種特殊的函式型別,每當從類或物件形成物件時,都會自動呼叫該函式。

  • 解構函式 - 指一種特殊的函式型別,每當物件或類被刪除或超出作用域時,都會自動呼叫該函式。

定義 Pascal 物件

物件使用型別宣告來宣告。物件宣告的一般形式如下:

type object-identifier = object  
   private
   field1 : field-type;  
   field2 : field-type;  
   ...
   public
   procedure proc1;  
   function f1(): function-type;
   end;  
var objectvar : object-identifier;

讓我們定義一個矩形物件,它有兩個整數型別的資料成員 - lengthwidth,以及一些用於操作這些資料成員的成員函式,以及一個用於繪製矩形的過程。

type 
   Rectangle = object  
   private  
      length, width: integer; 
   
   public  
      constructor init;  
      destructor done;  
      
      procedure setlength(l: inteter);  
      function getlength(): integer;  
      
      procedure setwidth(w: integer);  
      function getwidth(): integer;  
      
      procedure draw;
end;
var
   r1: Rectangle;
   pr1: ^Rectangle;

建立物件後,您將能夠呼叫與該物件相關的成員函式。一個成員函式只能處理相關物件的成員變數。

以下示例顯示瞭如何為兩個矩形物件設定長度和寬度,並透過呼叫成員函式來繪製它們。

r1.setlength(3);
r1.setwidth(7);

writeln(' Draw a rectangle: ', r1.getlength(), ' by ' , r1.getwidth());
r1.draw;
new(pr1);
pr1^.setlength(5);
pr1^.setwidth(4);

writeln(' Draw a rectangle: ', pr1^.getlength(), ' by ' ,pr1^.getwidth());
pr1^.draw;
dispose(pr1);

以下是一個完整的示例,展示瞭如何在 Pascal 中使用物件:

program exObjects;
type 
   Rectangle = object  
   private  
      length, width: integer; 
   
   public  
      procedure setlength(l: integer);
      function getlength(): integer;  
      
      procedure setwidth(w: integer);  
      function getwidth(): integer;  
      
      procedure draw;
end;
var
   r1: Rectangle;
   pr1: ^Rectangle;

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.setlength(3);
   r1.setwidth(7);
   
   writeln('Draw a rectangle:', r1.getlength(), ' by ' , r1.getwidth());
   r1.draw;
   new(pr1);
   pr1^.setlength(5);
   pr1^.setwidth(4);
   
   writeln('Draw a rectangle:', pr1^.getlength(), ' by ' ,pr1^.getwidth());
   pr1^.draw;
   dispose(pr1);
end.

當以上程式碼編譯並執行時,會產生以下結果:

Draw a rectangle: 3 by 7
* * * * * * *
* * * * * * *
* * * * * * *
Draw a rectangle: 5 by 4
* * * *
* * * *
* * * *
* * * *
* * * *

物件成員的可見性

可見性指示物件成員的可訪問性。Pascal 物件成員具有三種類型的可見性:

序號 可見性和可訪問性
1

Public

成員可被程式單元之外的其他單元使用

2

Private

成員僅在當前單元中可訪問。

3

Protected

成員僅對從父物件派生的物件可用。

預設情況下,物件的欄位和方法是 public 的,並且匯出到當前單元之外。

Pascal 物件的建構函式和解構函式:

建構函式是一種特殊的型別的方法,每當建立物件時都會自動呼叫它。您只需使用關鍵字 constructor 宣告方法即可在 Pascal 中建立建構函式。按照慣例,方法名稱為 Init,但是,您可以提供您自己的任何有效識別符號。您可以將任意數量的引數傳遞給建構函式。

解構函式是在物件銷燬期間呼叫的方法。解構函式銷燬建構函式建立的任何記憶體分配。

以下示例將為 Rectangle 類提供一個建構函式和一個解構函式,該函式將在物件建立時初始化矩形的長度和寬度,並在其超出作用域時銷燬它。

program exObjects;
type 
   Rectangle = object  
   private  
      length, width: integer; 
   public  
      constructor init(l, w: integer);
      destructor done;
      
      procedure setlength(l: integer);
      function getlength(): integer;  
      
      procedure setwidth(w: integer);  
      function getwidth(): integer;  
      
      procedure draw;
end;

var
   r1: Rectangle;
   pr1: ^Rectangle;

constructor Rectangle.init(l, w: integer);
begin
   length := l;
   width := w;
end;

destructor Rectangle.done;
begin
   writeln(' Desctructor Called');
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.init(3, 7);
   writeln('Draw a rectangle:', r1.getlength(), ' by ' , r1.getwidth());
   r1.draw;
   new(pr1, init(5, 4));
   
   writeln('Draw a rectangle:', pr1^.getlength(), ' by ',pr1^.getwidth());
   pr1^.draw;
   pr1^.init(7, 9);
   
   writeln('Draw a rectangle:', pr1^.getlength(), ' by ' ,pr1^.getwidth());
   pr1^.draw;
   dispose(pr1);
   r1.done;
end.

當以上程式碼編譯並執行時,會產生以下結果:

Draw a rectangle: 3 by 7
* * * * * * *
* * * * * * *
* * * * * * *
Draw a rectangle: 5 by 4
* * * *
* * * *
* * * *
* * * *
* * * *
Draw a rectangle: 7 by 9
* * * * * * * * *
* * * * * * * * *
* * * * * * * * *
* * * * * * * * *
* * * * * * * * *
* * * * * * * * *
* * * * * * * * *
Destructor Called

Pascal 物件的繼承

Pascal 物件可以選擇從父物件繼承。以下程式說明了 Pascal 物件中的繼承。讓我們建立另一個名為 TableTop 的物件,它繼承自 Rectangle 物件。

program exObjects;
type 
   Rectangle = object  
   private  
      length, width: integer; 
   public  
      procedure setlength(l: integer);  
      function getlength(): integer;  
      procedure setwidth(w: integer);  
      function getwidth(): integer;  
      procedure draw;
end;

TableTop = object (Rectangle)
   private
     material: string;
   public
      function getmaterial(): string;
      procedure setmaterial( m: string);
      procedure displaydetails;
      procedure draw;
end;

var
   tt1: TableTop;

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;

function TableTop.getmaterial(): string;
begin
   getmaterial := material;
end;

procedure TableTop.setmaterial( m: string);
begin
   material := m;
end;

procedure TableTop.displaydetails;
begin
   writeln('Table Top: ', self.getlength(), ' by ' , self.getwidth());
   writeln('Material: ', self.getmaterial());
end;

procedure TableTop.draw();
var
   i, j: integer;
begin
   for i:= 1 to length do
   begin
      for j:= 1 to width do
         write(' * ');
   writeln;
   end;
   writeln('Material: ', material);
end;

begin
   tt1.setlength(3);
   tt1.setwidth(7);
   tt1.setmaterial('Wood');
   tt1.displaydetails();
   writeln;
   writeln('Calling the Draw method');
   tt1.draw();
end.

以下應注意的重要事項:

  • 物件Tabletop 繼承了 Rectangle 物件的所有成員。

  • TableTop 中也存在一個 draw 方法。當使用 TableTop 物件呼叫 draw 方法時,將呼叫 TableTop 的 draw。

  • 有一個名為self的隱式例項,它引用物件的當前例項。

當以上程式碼編譯並執行時,會產生以下結果:

Table Top: 3 by 7
Material: Wood

Calling the Draw Method 
* * * * * * *
* * * * * * *
* * * * * * *
Material: Wood
廣告