Programmierkurs: Delphi: Pascal: Klassen

Aus Wikibooks

Wechseln zu: Navigation, Suche

Inhaltsverzeichnis

[Bearbeiten] Klassen

[Bearbeiten] Einleitung

Die Grundlage für die objektorientierte Programmierung, kurz OOP, bilden Klassen. Diese kapseln, ähnlich wie Records, verschiedene Daten zu einer Struktur. Gleichzeitig liefern sie Methoden mit, welche die vorhandenen Daten bearbeiten.

Der Vorteil von Klassen besteht also darin, dass man mit ihnen zusammengehörige Variablen, Funktionen und Prozeduren zusammenfassen kann. Weiterhin können - bei entsprechender Implementation - die Daten einer Klasse nicht „von außen“ geändert werden.

[Bearbeiten] Aufbau einer Klasse

[Bearbeiten] Allgemeiner Aufbau

Die einfachste Klasse entspricht in ihrem Aufbau einem Record:

program Klassentest;

type
  TMyRec = record
    EinByte: Byte;
    EinString: string;
  end;

  TMyClass = class
    FEinByte: Byte;
    FEinString: string;
  end;

var
  MyRec: TMyRec;
  MyClass: TMyClass;

begin
  MyRec.EinByte := 15;
  MyClass := TMyClass.Create;
  MyClass.FEinString := 'Hallo Welt!';
  MyClass.Free;   // bzw. MyClass.Destroy;
end.

Hierbei kann man bereits einen Unterschied zu Records erkennen: Während man jederzeit auf die Daten eines Records zugreifen kann, muss bei einer Klasse zunächst Speicher angefordert und zum Schluss wieder freigeben werden. Die speziellen Methoden Create und Destroy bzw. Free sind in jeder Klasse enthalten und müssen nicht gesondert programmiert werden. Zur Freigabe des Speichers sollte dabei Free bevorzugt verwendet werden. Hierzu mehr im Kapitel Konstruktoren und Destruktoren.

Weiterhin hat es sich durchgesetzt, die Variablen einer Klasse, Felder genannt, immer mit dem Buchstaben F zu beginnen. So werden Verwechselungen mit globalen und lokalen Variablen vermieden.

[Bearbeiten] Sichtbarkeit der Daten

Im oben gezeigten Beispiel kann auf die Felder wie bei einem Record zugegriffen werden. Dies sollte man unter allen Umständen vermeiden!

Hierfür gibt es eine ganze Reihe von Möglichkeiten. Zunächst einmal können Felder, sowie Methoden und Eigenschaften einer Klasse nach außen hin „versteckt“ werden. Das erreicht man mit folgenden Schlüsselwörtern:

  • strict private - auf diese Daten kann außerhalb der Klasse nicht zugegriffen werden
  • private - wie strict private, allerdings kann auch in der beinhaltenden Unit darauf zugegriffen werden
  • strict protected - hierauf kann man nur innerhalb der Klasse und ihrer Nachfahren zugreifen
  • protected - wie strict protected, allerdings kann auch in der beinhaltenden Unit darauf zugegriffen werden
  • public - diese Daten sind uneingeschränkt zugänglich
  • published - zusätzlich zu public können diese Daten auch im Objekt-Inspektor von Delphi™ bearbeitet werden (nur bei Eigenschaften von Komponenten sinnvoll).

Das entsprechende Schlüsselwort wird der Gruppe von Daten vorangestellt, für die diese Sichtbarkeit gelten soll. Um die Felder unserer Klasse zu verstecken, schreiben wir also:

type
  TMyClass = class
  private
    FEinByte: Byte;
    FEinString: string;
  end;

Wenn man jetzt versucht, wie oben gezeigt, einem Feld einen Wert zuzuweisen oder ihn auszulesen, wird bereits bei der Kompilierung eine Fehlermeldung ausgegeben.

[Bearbeiten] Methoden

Wie erhält man nun aber Zugriff auf die Daten? Dies erreicht man über öffentlich zugängliche Methoden, mit denen die Daten ausgelesen und geändert werden können.

Eine Methode ist eine fest mit der Klasse verbundene Funktion oder Prozedur. Daher wird sie auch wie Felder direkt innerhalb der Klasse definiert:

TMyClass = class
public
  function GetString: string;
  procedure SetString(NewStr: string);
end;

Die Ausführung der Methoden erfolgt direkt über die Variable dieses Klassentyps:

var
  MyClass: TMyClass;

...

MyClass.SetString('Hallo Welt!');
WriteLn(MyClass.GetString);

In der Typdefinition werden nur der Name und die Parameter von Methoden definiert. Die Implementation, also die Umsetzung dessen, was eine Methode tun soll, erfolgt außerhalb der Klasse, genau wie bei globalen Funktionen und Prozeduren. Allerdings muss der Klassenname vorangestellt werden:

function TMyClass.GetString: string;
begin
  Result := FEinString;
end;

procedure TMyClass.SetString(NewStr: string);
begin
  FEinString := NewStr;
end;

Da die Methoden GetString und SetString Mitglieder der Klasse sind, können diese auf das private Feld FEinString zugreifen.

Ebenso wie globale Funktionen und Prozeduren lassen sich auch Methoden überladen. Dies bedeutet, dass mehrere Prozeduren mit dem gleichen Namen aber unterschiedlichen Parametern innerhalb einer Klasse deklariert werden. Hierzu ein vollständiges Programm als Beispiel:

program Ueberladen;

{$APPTYPE CONSOLE}

uses
  SysUtils;

type
  TTestKlasse = class
  public
    function ZaehleStellen(zahl: Cardinal): Integer; overload;
    function ZaehleStellen(wort: string): Integer; overload;
  end;

function TTestKlasse.ZaehleStellen(zahl: Cardinal): Integer;
begin
  Result := Length(IntToStr(zahl));
end;

function TTestKlasse.ZaehleStellen(wort: string): Integer;
begin
  Result := Length(wort);
end;

var
  Zaehler: TTestKlasse;

begin
  Zaehler := TTestKlasse.Create;

  Writeln(Zaehler.ZaehleStellen(16384));
  Writeln(Zaehler.ZaehleStellen('Donnerstag'));
  Readln;
  Zaehler.Free;
end.

Im ersten Fall – Writeln(ZaehleStellen(16384)); – wird die Methode TTestKlasse.ZaehleStellen(zahl: Cardinal): Integer aufgerufen, da der Übergabeparameter vom Typ Cardinal ist. Es wird 5 ausgegeben.

Im zweiten Fall – Writeln(ZaehleStellen('Donnerstag')); – wird die Methode TTestKlasse.ZaehleStellen(wort: string): Integer aufgerufen, da der Übergabeparameter ein String ist. Dementsprechend wird der Wert 10 ausgegeben.

In beiden Fällen wird die Stellenanzahl mittels Length bestimmt. Da Length aber eine Zeichenkette erwartet, wird der Zahlwert im ersten Fall zunächst in eine Zeichenkette umgewandelt und dann die Länge dieser Zeichenkette bestimmt.

Näheres zum Überladen unter Prozeduren und Funktionen.

[Bearbeiten] Eigenschaften

Für unser Beispiel erscheint der Zugriff etwas umständlich. Daher gibt es noch eine andere einfache Möglichkeit, Daten einer Klasse auszulesen und zu ändern: Eigenschaften (engl. properties).

Eigenschaften lassen sich wie Variablen behandeln, das heißt, man kann sie (wenn gewünscht) auslesen oder (wenn gewünscht) ändern. Die interne Umsetzung bleibt dabei verborgen. So kann eine Eigenschaft zum Beispiel direkt auf ein Feld oder über den Umweg einer Methode darauf zugreifen:

type
  TMyClass = class
  private
    FEinInt: Integer;
    function HoleDoppelteZahl: Integer;
    procedure SpeichereHalbeZahl(DoppZahl: Integer);    // Name des Parameters ist egal
  public
    property Zahl: Integer read FEinInt write FEinInt;
    property DoppelZahl: Integer read HoleDoppelteZahl write SpeichereHalbeZahl;
  end;

Sowohl das Feld als auch die Methoden sind versteckt. Die einzige Verbindung zur Außenwelt besteht über die Eigenschaften. Hier greift die Eigenschaft „Zahl“ beim Lesen und Schreiben direkt auf das Feld zu, während „DoppelZahl“ in beiden Fällen auf Methoden zurückgreift. Da DoppelZahl immer das Doppelte von Zahl sein soll (so sagt es zumindest der Name), werden die Methoden wie folgt implementiert:

function HoleDoppelteZahl: Integer;
begin
  Result := FEinInt * 2;
end;

procedure SpeichereHalbeZahl(DoppZahl: Integer);
begin
  FEinInt := DoppZahl div 2;
end;

Diese Klasse führt Berechnungen durch, ohne dass es der Benutzer mitbekommt. Weist man Zahl einen Wert zu, erhält man aus DoppelZahl automatisch das Doppelte. Andersherum kann man DoppelZahl einen Wert zuweisen und erhält mit Zahl die Hälfte. Von außen betrachtet erscheinen beide Eigenschaften jedoch wie Felder:

with MyClass do
begin
  Zahl := 4;
  WriteLn(DoppelZahl);  // Schreibt eine 8
  DoppelZahl := 20;
  WriteLn(Zahl);        // ergibt 10 und nicht etwa 4
end;

Soll DoppelZahl einen Schreibschutz erhalten, lässt man in der Definition den Teil „write ...“ weg. Damit kann auch die entsprechende Methode SpeichereHalbeZahl entfernt werden.

Die verschiedenen Arten von Eigenschaften werden ausführlicher in einem eigenen Kapitel behandelt.


Arrow left.png Pascal: Threads Inhaltsverzeichnis Pascal: Konstruktoren und Destruktoren Arrow right.png
Persönliche Werkzeuge