Pascal - Klassen
Sie haben gesehen, dass Pascal-Objekte einige Merkmale eines objektorientierten Paradigmas aufweisen. Sie implementieren Kapselung, Verstecken von Daten und Vererbung, haben aber auch Einschränkungen. Zum Beispiel nehmen Pascal-Objekte nicht am Polymorphismus teil. Daher werden Klassen häufig verwendet, um ein korrektes objektorientiertes Verhalten in einem Programm zu implementieren, insbesondere in der GUI-basierten Software.
Eine Klasse wird fast genauso definiert wie ein Objekt, ist jedoch eher ein Zeiger auf ein Objekt als auf das Objekt selbst. Technisch bedeutet dies, dass die Klasse auf dem Heap eines Programms zugewiesen wird, während das Objekt auf dem Stapel zugewiesen wird. Mit anderen Worten, wenn Sie eine Variable als Objekttyp deklarieren, nimmt sie auf dem Stapel so viel Platz ein wie die Größe des Objekts. Wenn Sie jedoch eine Variable vom Klassentyp deklarieren, nimmt sie immer die Größe eines Zeigers an auf dem Stapel. Die tatsächlichen Klassendaten befinden sich auf dem Heap.
Pascal-Klassen definieren
Eine Klasse wird wie ein Objekt mithilfe der Typdeklaration deklariert. Die allgemeine Form einer Klassendeklaration lautet wie folgt:
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;
Es lohnt sich, folgende wichtige Punkte zu beachten:
Klassendefinitionen sollten nur unter den Typdeklarationsteil des Programms fallen.
Eine Klasse wird mit dem definiert class Stichwort.
Felder sind Datenelemente, die in jeder Instanz der Klasse vorhanden sind.
Methoden werden innerhalb der Definition einer Klasse deklariert.
Es gibt einen vordefinierten Konstruktor namens Createin der Root-Klasse. Jede abstrakte Klasse und jede konkrete Klasse ist ein Nachkomme von Root, daher haben alle Klassen mindestens einen Konstruktor.
Es gibt einen vordefinierten Destruktor namens Destroyin der Root-Klasse. Jede abstrakte Klasse und jede konkrete Klasse ist ein Nachkomme von Root, daher haben alle Klassen mindestens einen Destruktor.
Definieren wir eine Rechteckklasse mit zwei Datenelementen vom Typ Integer - Länge und Breite sowie einige Elementfunktionen zum Bearbeiten dieser Datenelemente und eine Prozedur zum Zeichnen des Rechtecks.
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;
Schreiben wir ein vollständiges Programm, das eine Instanz einer Rechteckklasse erstellt und das Rechteck zeichnet. Dies ist das gleiche Beispiel, das wir bei der Erörterung von Pascal-Objekten verwendet haben. Sie werden feststellen, dass beide Programme mit den folgenden Ausnahmen fast gleich sind:
Sie müssen die Anweisung {$ mode objfpc} einschließen, um die Klassen verwenden zu können.
Sie müssen die Anweisung {$ m +} für die Verwendung von Konstruktoren einfügen.
Die Klasseninstanziierung unterscheidet sich von der Objektinstanziierung. Nur das Deklarieren der Variablen schafft keinen Platz für die Instanz. Sie verwenden den Konstruktor create, um Speicher zuzuweisen.
Hier ist das vollständige Beispiel -
{$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.
Wenn der obige Code kompiliert und ausgeführt wird, ergibt sich das folgende Ergebnis:
Draw Rectangle: 3 by 7
* * * * * * *
* * * * * * *
* * * * * * *
Draw Rectangle: 4 by 6
* * * * * *
* * * * * *
* * * * * *
* * * * * *
Sichtbarkeit der Klassenmitglieder
Sichtbarkeit gibt die Zugänglichkeit der Klassenmitglieder an. Pascal-Klassenmitglieder haben fünf Arten der Sichtbarkeit -
Sr.Nr. | Sichtbarkeit und Zugänglichkeit |
---|---|
1 |
Public Diese Mitglieder sind immer erreichbar. |
2 |
Private Auf diese Mitglieder kann nur in dem Modul oder der Einheit zugegriffen werden, die die Klassendefinition enthält. Sie können von innerhalb oder außerhalb der Klassenmethoden aufgerufen werden. |
3 | Strict Private Auf diese Mitglieder kann nur über Methoden der Klasse selbst zugegriffen werden. Andere Klassen oder untergeordnete Klassen in derselben Einheit können nicht auf sie zugreifen. |
4 |
Protected Dies ist dasselbe wie privat, außer dass diese Mitglieder für absteigende Typen zugänglich sind, selbst wenn sie in anderen Modulen implementiert sind. |
5 | Published Dies ist dasselbe wie ein Public, aber der Compiler generiert Typinformationen, die für das automatische Streaming dieser Klassen erforderlich sind, wenn sich der Compiler im Status {$ M +} befindet. In einem veröffentlichten Abschnitt definierte Felder müssen vom Klassentyp sein. |
Konstruktoren und Destruktoren für Pascal-Klassen
Konstruktoren sind spezielle Methoden, die automatisch aufgerufen werden, wenn ein Objekt erstellt wird. Wir nutzen dieses Verhalten also voll aus, indem wir viele Dinge durch Konstruktorfunktionen initialisieren.
Pascal bietet eine spezielle Funktion namens create () zum Definieren eines Konstruktors. Sie können beliebig viele Argumente an die Konstruktorfunktion übergeben.
Im folgenden Beispiel wird ein Konstruktor für eine Klasse mit dem Namen "Bücher" erstellt und der Preis und der Titel für das Buch zum Zeitpunkt der Objekterstellung initialisiert.
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.
Wenn der obige Code kompiliert und ausgeführt wird, ergibt sich das folgende Ergebnis:
Title: Physics for High School
Price: 10
Title: Advanced Chemistry
Price: 15
Title: Algebra
Price: 7
Wie der implizite Konstruktor namens create gibt es auch eine implizite Destruktormethode destroy, mit der Sie alle in der Klasse verwendeten Ressourcen freigeben können.
Erbe
Pascal-Klassendefinitionen können optional von einer übergeordneten Klassendefinition erben. Die Syntax lautet wie folgt:
type
childClas-identifier = class(baseClass-identifier)
< members >
end;
Das folgende Beispiel enthält eine Romanklasse, die die Books-Klasse erbt und je nach Anforderung weitere Funktionen hinzufügt.
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.
Wenn der obige Code kompiliert und ausgeführt wird, ergibt sich das folgende Ergebnis:
Title: Gone with the Wind
Price: 375.99
Author: Margaret Mitchell
Title: Atlas Shrugged
Price: 467.75
Author: Ayn Rand
Es lohnt sich, folgende wichtige Punkte zu beachten:
Die Mitglieder der Bücherklasse haben protected Sichtweite.
Die Novels-Klasse hat zwei Konstruktoren, also die overload Der Operator wird zum Überladen von Funktionen verwendet.
Das Books.Display-Verfahren wurde deklariert virtual, so dass die gleiche Methode aus der Novels-Klasse kann override es.
Der Novels.Create-Konstruktor ruft den Basisklassenkonstruktor mit dem auf inherited Stichwort.
Schnittstellen
Schnittstellen werden definiert, um den Implementierern einen gemeinsamen Funktionsnamen bereitzustellen. Verschiedene Implementierer können diese Schnittstellen entsprechend ihren Anforderungen implementieren. Man kann sagen, Schnittstellen sind Skelette, die von Entwicklern implementiert werden. Das Folgende ist ein Beispiel für eine Schnittstelle -
type
Mail = Interface
Procedure SendMail;
Procedure GetMail;
end;
Report = Class(TInterfacedObject, Mail)
Procedure SendMail;
Procedure GetMail;
end;
Beachten Sie, dass eine Klasse, wenn sie eine Schnittstelle implementiert, alle Methoden der Schnittstelle implementieren sollte. Wenn eine Methode einer Schnittstelle nicht implementiert ist, gibt der Compiler einen Fehler aus.
Abstrakte Klassen
Eine abstrakte Klasse kann nicht instanziiert, sondern nur vererbt werden. Eine abstrakte Klasse wird angegeben, indem das Wortsymbol abstrakt wie folgt in die Klassendefinition aufgenommen wird:
type
Shape = ABSTRACT CLASS (Root)
Procedure draw; ABSTRACT;
...
end;
Beim Erben von einer abstrakten Klasse müssen alle in der Klassendeklaration des Elternteils als abstrakt gekennzeichneten Methoden vom Kind definiert werden. Darüber hinaus müssen diese Methoden mit derselben Sichtbarkeit definiert werden.
Statisches Schlüsselwort
Wenn Sie Klassenmitglieder oder Methoden als statisch deklarieren, können Sie auf sie zugreifen, ohne dass eine Instanziierung der Klasse erforderlich ist. Auf ein als statisch deklariertes Mitglied kann nicht mit einem instanziierten Klassenobjekt zugegriffen werden (obwohl dies mit einer statischen Methode möglich ist). Das folgende Beispiel veranschaulicht das Konzept -
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.
Wenn der obige Code kompiliert und ausgeführt wird, ergibt sich das folgende Ergebnis:
12
31
31
51
51
Sie müssen die Direktive {$ static on} verwenden, um die statischen Elemente zu verwenden.