Pascal - Zajęcia

Widzieliście, że obiekty Pascal wykazują pewne cechy paradygmatu zorientowanego obiektowo. Wdrażają enkapsulację, ukrywanie danych i dziedziczenie, ale mają też ograniczenia. Na przykład obiekty Pascal nie biorą udziału w polimorfizmie. Dlatego klasy są szeroko stosowane do implementacji prawidłowego zachowania zorientowanego obiektowo w programie, zwłaszcza w oprogramowaniu opartym na graficznym interfejsie użytkownika.

Klasa jest definiowana prawie tak samo jak obiekt, ale jest wskaźnikiem do obiektu, a nie do samego obiektu. Z technicznego punktu widzenia oznacza to, że klasa jest przydzielona na stercie programu, podczas gdy obiekt jest przydzielony na stosie. Innymi słowy, kiedy deklarujesz zmienną jako typ obiektu, zajmie ona tyle miejsca na stosie, ile rozmiar obiektu, ale kiedy deklarujesz zmienną typu klasy, zawsze będzie miała rozmiar wskaźnika na stosie. Rzeczywiste dane klasy będą na stercie.

Definiowanie klas Pascala

Klasa jest deklarowana w taki sam sposób jak obiekt, przy użyciu deklaracji typu. Ogólna forma deklaracji klasy jest następująca -

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;

Warto zwrócić uwagę na następujące ważne punkty -

  • Definicje klas powinny znajdować się tylko w części programu zawierającej deklarację typu.

  • Klasa jest definiowana przy użyciu class słowo kluczowe.

  • Pola to elementy danych, które istnieją w każdym wystąpieniu klasy.

  • Metody są zadeklarowane w definicji klasy.

  • Istnieje predefiniowany konstruktor o nazwie Createw klasie Root. Każda klasa abstrakcyjna i każda klasa konkretna jest potomkiem klasy Root, więc wszystkie klasy mają co najmniej jednego konstruktora.

  • Istnieje predefiniowany destruktor o nazwie Destroyw klasie Root. Każda klasa abstrakcyjna i każda klasa konkretna jest potomkiem klasy Root, więc wszystkie klasy mają co najmniej jeden destruktor.

Zdefiniujmy klasę Rectangle, która ma dwa elementy składowe danych typu całkowitego - długość i szerokość oraz niektóre funkcje składowe służące do manipulowania tymi elementami składowymi danych oraz procedurę rysowania prostokąta.

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;

Napiszmy kompletny program, który utworzy instancję klasy rectangle i narysuje prostokąt. To jest ten sam przykład, którego użyliśmy podczas omawiania obiektów Pascal. Przekonasz się, że oba programy są prawie takie same, z następującymi wyjątkami -

  • Aby używać klas, musisz dołączyć dyrektywę {$ mode objfpc}.

  • Aby używać konstruktorów, musisz dołączyć dyrektywę {$ m +}.

  • Tworzenie instancji klasy różni się od tworzenia instancji obiektu. Tylko zadeklarowanie zmiennej nie tworzy miejsca na instancję, użyjesz konstruktora create do przydzielenia pamięci.

Oto pełny przykład -

{$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.

Kiedy powyższy kod jest kompilowany i wykonywany, daje następujący wynik -

Draw Rectangle: 3 by 7
* * * * * * *
* * * * * * *
* * * * * * *
Draw Rectangle: 4 by 6
* * * * * * 
* * * * * * 
* * * * * * 
* * * * * *

Widoczność członków klasy

Widoczność wskazuje na dostępność członków klasy. Członkowie klasy Pascal mają pięć typów widoczności -

Sr.No Widoczność i dostępność
1

Public

Ci członkowie są zawsze dostępni.

2

Private

Dostęp do tych elementów członkowskich można uzyskać tylko w module lub jednostce, która zawiera definicję klasy. Dostęp do nich można uzyskać z wewnątrz metod klas lub spoza nich.

3

Strict Private

Dostęp do tych elementów członkowskich można uzyskać tylko z metod samej klasy. Inne klasy lub klasy potomne w tej samej jednostce nie mają do nich dostępu.

4

Protected

Działa tak samo jak private, z tym wyjątkiem, że te elementy członkowskie są dostępne dla typów podrzędnych, nawet jeśli są zaimplementowane w innych modułach.

5

Published

Działa tak samo jak Public, ale kompilator generuje informacje o typie, które są potrzebne do automatycznego przesyłania strumieniowego tych klas, jeśli kompilator jest w stanie {$ M +}. Pola zdefiniowane w opublikowanej sekcji muszą być typu klasowego.

Konstruktory i destruktory dla klas Pascal

Konstruktory to specjalne metody, które są wywoływane automatycznie przy każdym utworzeniu obiektu. Dlatego w pełni wykorzystujemy to zachowanie, inicjując wiele rzeczy za pomocą funkcji konstruktora.

Pascal udostępnia specjalną funkcję o nazwie create () do definiowania konstruktora. Do funkcji konstruktora można przekazać dowolną liczbę argumentów.

Poniższy przykład utworzy jeden konstruktor dla klasy o nazwie Książki i zainicjuje cenę i tytuł książki w momencie tworzenia obiektu.

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.

Kiedy powyższy kod jest kompilowany i wykonywany, daje następujący wynik -

Title: Physics for High School
Price: 10
Title: Advanced Chemistry
Price: 15
Title: Algebra
Price: 7

Podobnie jak w przypadku niejawnego konstruktora o nazwie create, istnieje również niejawna metoda destructor niszcząca, za pomocą której można zwolnić wszystkie zasoby używane w klasie.

Dziedzictwo

Definicje klas Pascala mogą opcjonalnie dziedziczyć z definicji klasy nadrzędnej. Składnia jest następująca -

type
childClas-identifier = class(baseClass-identifier) 
< members >
end;

Poniższy przykład przedstawia klasę powieści, która dziedziczy klasę Books i dodaje więcej funkcji w oparciu o wymaganie.

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.

Kiedy powyższy kod jest kompilowany i wykonywany, daje następujący wynik -

Title: Gone with the Wind
Price: 375.99
Author: Margaret Mitchell
Title: Atlas Shrugged
Price: 467.75
Author: Ayn Rand

Warto zwrócić uwagę na następujące ważne punkty -

  • Członkowie klasy Książki mają protected widoczność.

  • Klasa Novels ma dwa konstruktory, więc overload operator służy do przeciążania funkcji.

  • Zadeklarowano procedurę Books.Display virtual, więc ta sama metoda z klasy Novels może override to.

  • Konstruktor Novels.Create wywołuje konstruktor klasy bazowej przy użyciu inherited słowo kluczowe.

Interfejsy

Interfejsy są zdefiniowane w celu zapewnienia implementującym wspólną nazwę funkcji. Różni realizatorzy mogą implementować te interfejsy zgodnie ze swoimi wymaganiami. Można powiedzieć, że interfejsy to szkielety, które są implementowane przez programistów. Poniżej znajduje się przykład interfejsu -

type  
   Mail = Interface  
      Procedure SendMail;  
      Procedure GetMail;  
   end;  
   
   Report = Class(TInterfacedObject,  Mail)  
      Procedure SendMail;  
      Procedure GetMail;  
   end;

Należy pamiętać, że gdy klasa implementuje interfejs, powinna implementować wszystkie metody interfejsu. Jeśli metoda interfejsu nie jest zaimplementowana, kompilator zgłosi błąd.

Klasy abstrakcyjne

Klasa abstrakcyjna to taka, której nie można utworzyć, a jedynie dziedziczyć. Klasa abstrakcyjna jest określana przez dołączenie abstrakcyjnego słowa symbol do definicji klasy, na przykład:

type
   Shape = ABSTRACT CLASS (Root)
      Procedure draw; ABSTRACT;
      ...
   end;

Podczas dziedziczenia z klasy abstrakcyjnej wszystkie metody oznaczone jako abstrakcyjne w deklaracji klasy rodzica muszą być zdefiniowane przez dziecko; dodatkowo metody te muszą być zdefiniowane z taką samą widocznością.

Statyczne słowo kluczowe

Zadeklarowanie elementów członkowskich lub metod klas jako statycznych sprawia, że ​​są one dostępne bez konieczności tworzenia wystąpienia klasy. Nie można uzyskać dostępu do elementu członkowskiego zadeklarowanego jako statyczny za pomocą utworzonego obiektu klasy (chociaż metoda statyczna może). Poniższy przykład ilustruje koncepcję -

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.

Kiedy powyższy kod jest kompilowany i wykonywany, daje następujący wynik -

12
31
31
51
51

Aby używać statycznych elementów członkowskich, musisz użyć dyrektywy {$ static on}.