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}.