Die Klasse als Datenstruktur
|
Willemers Informatik-Ecke
Daten zusammensetzen
Mit einer Klasse
können Sie mehrere Daten zusammenfassen, um Ihren eigenen Datentyp zu
modellieren. Dabei dürfen auch Arrays oder andere selbst erstellte Datentypen
hinzugenommen werden. Der Phantasie sind keine Grenzen gesetzt.
Es geht darum, die Daten eines realen Objekts in einer Klasse
so zusammenfassen, dass ein brauchbares Modell der Wirklichkeit im
Programm entsteht.
Beispiel
Betrachten Sie als einfaches Beispiel ein Datum. Es besteht aus
drei ganzen Zahlen, die jeweils bequem in eine Integer-Variable passen.
[Die Klasse Datum -- erster Anlauf]
class tDatum
{
public:
int Tag;
int Monat;
int Jahr;
};
Das Wort public mit dem Doppelpunkt bedeutet, dass alle nachfolgenden
Elemente der Klasse öffentlich zugänglich sind. Vergessen Sie dieses Wort
bitte nicht, denn in einer Klasse ist ansonsten alles privat. Das bedeutet,
dass Sie an die Daten nicht herankommen. Wozu so etwas gut ist, werden wir
später noch sehen. Der Befehl public: heißt hier
einfach, dass Sie auf Tag, Monat und Jahr genau so zugreifen können, wie
Sie es von der Struktur gewohnt sind.
tDatum heute;
heute.Tag = 13;
Elementzugriff
Hier wird das Objekt heute vom Typ tDatum angelegt.
Das Objekt enthält die drei Integer-Variablen Tag,
Monat und
Jahr, wie es in der Klassendefinition zu sehen ist.
Um auf eine Elementvariable zugreifen zu können, wird
an den Objektnamen ein Punkt und dann der in der Klassendefinition
verwendete Elementname gehängt.
Im Beispiel wird der Tag auf 13 gesetzt.
Zeiger ->
Um mit einem Zeiger auf Klassenelemente zu verweisen, muss zunächst mit
Hilfe des Sterns dereferenziert werden und anschließend nach einem Punkt
das Element benannt werden.
Da die Assoziation zum Punkt stärker ist als der Stern, muss
eine Klammer dem Stern die notwendige Priorität verleihen.
Da dieser Zugriff doch recht unansehnlich ist, verwendet man üblicherweise
stattdessen eine Kombination aus Minus- und Größer-Zeichen, das dann einem
Pfeil ähnlich sieht.
tDatum *irgendwann;
(*irgendwann).Tag = 13;
irgendwann->Tag = 13;
Die Variable irgendwann ist ein Zeiger auf ein
Objekt der Klasse tDatum. Dargestellt sind die beiden Alternativen,
mit einem Zeiger auf das Element Tag zuzugreifen.
Eine Klasse ist die abstrakte Beschreibung eines Datentyps.
Ein Objekt ist eine Variable, deren Datentyp eine Klasse ist.
Man sagt auch: Ein Objekt ist die Instanz einer Klasse.
Funktion und Datenstruktur heiraten
Elementfunktionen
Nun können Sie ein Datum in einem selbst erstellten Datentyp ablegen.
Dazu erzeugen Sie eine Variable, also ein Objekt der Klasse tDatum.
Aber was nützt Ihnen die schönste Datenstruktur in Ihrem Programm, wenn sie
nicht durch Funktionen zum Leben erweckt wird?
Sie werden das Datum eingeben und ausgeben wollen.
Vielleicht wollen Sie
den Wochentag eines Datums ermitteln oder gar den Ostertermin.
Sie wollen das heutige Datum bestimmen und errechnen, welches Datum
14 Tage später ist.
Kurz gesagt, ein Datenverbund ist nichts wert ohne Funktionen, die auf ihn
wirken. Aber die Funktionen sind auch nur im Zusammenhang mit ihrem
Datenverbund sinnvoll. So ist die Berechnung des heutigen Datums nur
in Verbindung mit einem Datum sinnvoll. Auch die Berechnung eines Wochentags
kann nirgends anders eingesetzt werden als im Zusammenhang mit einem Datum.
Aus diesem Grund werden die Funktionen ebenso in die Klasse integriert
wie die Datenelemente.
Eine Funktion, die zu einer Klasse gehört, nennt man Elementfunktion oder
auf englisch member function. In anderen objektorientierten Programmiersprachen
spricht man auch von einer Methode oder Operation.
Aufruf
So, wie Sie auf Datenelemente nur über ein real existierendes Objekt, also eine
Variable dieser Klasse zugreifen können, kann auch eine Funktion nur über ein
Objekt aufgerufen werden. Objekt und Funktionsnamen werden dabei durch einen
Punkt getrennt. Die Funktion arbeitet mit den
Daten des Objekts, über das sie gerufen wurde. Aus prozeduraler Sicht könnte
man es so sehen, dass eine Elementfunktion immer bereits einen Parameter mit
sich trägt, nämlich das Objekt, über das sie aufgerufen wurde.
NeuJahr()
Fangen wir damit an, der Datumsklasse eine Funktion zur Berechnung des
Neujahrstages hinzuzufügen. Die Funktion wird genauso in die
Klassendefinition eingebunden wie die drei Integer-Bestandteile.
class tDatum
{
public:
int Tag;
int Monat;
int Jahr;
void NeuJahr(int Jahr);
};
heute.NeuJahr(2000);
Die Funktion NeuJahr() benötigt als Parameter die Jahreszahl.
Da sie eine Elementfunktion der Datumsklasse ist, wird sie über ein
Objekt vom Typ tDatum aufgerufen. Im Beispiel ist das
das Objekt heute. Die Elementfunktion kann direkt auf die Elemente
der Klasse zugreifen. Alle Änderungen, die sie in den Datenelementen
durchführt, wirken auf das Objekt heute. Das heißt,
nach diesem Aufruf werden die Elemente des Objekts heute
so aussehen: Tag enthält wie Monat eine 1, und
Jahr ist 2000.
Der Funktionsname wird beim Aufruf genauso an das Objekt gehängt, wie das
bei Datenelementen gemacht wird. Da die Funktion mit dem Objekt direkt
verbunden ist, braucht das Objekt nicht als Parameter übergeben werden.
Das folgende Listing zeigt, wie die Funktion NeuJahr() in der
Klassendefinition deklariert wird und wie anschließend die Definition der
Funktion aussieht.
[Klasse mit Elementfunktion]
class tDatum
{
public:
int Tag;
int Monat;
int Jahr;
void NeuJahr(int Jahr);
};
void tDatum::NeuJahr(int pJahr)
{
Tag = 1;
Monat = 1;
Jahr = pJahr;
}
Um die Elementfunktion NeuJahr() von einer globalen Funktion
zu unterscheiden, wird, wie oben zu sehen, dem Namen der Klassenname
vorangestellt und durch zwei Doppelpunkte vom Funktionsnamen abgetrennt.
Definition in der Klasse
Alternativ können Sie die Elementfunktion auch direkt in der Klasse definieren.
Das wird leicht unübersichtlich, darum sollten Sie das nur bei sehr kurzen
Funktionen tun.
[Klasse mit Elementfunktion]
class tDatum
{
public:
int Tag;
int Monat;
int Jahr;
void NeuJahr(int pJahr)
{
Tag = 1;
Monat = 1;
Jahr = pJahr;
}
};
Automatisch inline
Eine Funktion, die in der Klassendefinition nicht nur deklariert, sondern
auch definiert ist, wird automatisch als
Inline-Funktion übersetzt.
Zugriff auf andere Elemente
In beiden Fällen greift die Elementfunktion direkt auf die Elementvariablen
des Objektes zu, über das die Elementfunktion aufgerufen wurde.
Ganz allgemein gilt, dass Elementfunktionen einer Klasse auf alle anderen
Elemente des gleichen Objekts zugreifen können, ohne den Klassennamen explizit
voranzustellen.
Nomenklatur
Die objektorientierte Programmierung hat einige neue Begriffe aufgebracht.
Objekt und Klasse
Der zentrale Begriff des Objekts bezeichnet einen Speicherbereich, der durch
eine Klasse beschrieben wird.
Prinzipiell kann sich der Anfänger ein Objekt als eine besondere Art einer
Variablen vorstellen und die Klasse als die Typbeschreibung.
Im Buch wird ein Objekt auch hin und wieder als Variable bezeichnet,
insbesondere dann, wenn sich ein Objekt an dieser Stelle wie jede andere
Variable verhält.
Attribut
Die Daten-Elemente einer Klasse werden in diesem Buch meist Elementvariable
genannt. In der objektorientierten Literatur findet sich dafür auch die
Bezeichnung Attribut.
Damit wird angedeutet, dass die Elementvariablen die Eigenschaften eines
Objekts beschreiben.
Methode und Operation
Die Funktionen einer Klasse, die hier als Elementfunktionen bezeichnet werden,
finden sich in der objektorientierten Literatur unter dem Namen Methode oder
Operation wieder.
Diese Bezeichnung bringt zum Ausdruck, dass die Funktion nur über das Objekt
erreichbar und damit eine Aktion des Objekts ist.
Konventionen im Buch
Ich habe mich zu dieser Namensgebung vor allem aus zwei Gründen entschlossen.
Zunächst einmal verwendet auch Bjarne Stroustrup, der Entwickler der Sprache
C++ auch diese Bezeichnungen.
Ferner ist es meiner Ansicht nach für Anfänger leichter, wenn ähnliche Dinge
ähnlich heißen.
Zugriff auf Klassenelemente
Innerhalb einer Elementfunktion kann auf die Elemente des Objekts direkt
zugegriffen werden. Sie können aber auch den vordefinierten
Selbstreferenzzeiger this verwenden. Damit drücken Sie aus, dass
Sie explizit ein Element dieses Objekts ansprechen wollen.
[Zugriff per this]
void tDatum::NeuJahr(int Jahr)
{
this->Tag = 1;
this->Monat = 1;
this->Jahr = Jahr;
}
Lokale Variablen, zu denen ja auch die Parameter gehören, überdecken
Klassenelemente gleichen Namens. Durch den Zeiger this kann auf die
Klassenelemente zugegriffen werden.
Sie sehen das besonders deutlich an der Zuweisung der Jahreszahl. Das Ziel
ist das Klassenelement, die Quelle ist der Parameter.
Alternativ kann auch über den Klassennamen zugegriffen werden. Dazu wird
ein doppelter Doppelpunkt zwischen den Klassennamen und den Elementnamen
gesetzt.
[Zugriff per Klassennamen]
void tDatum::NeuJahr(int Jahr)
{
tDatum::Tag = 1;
tDatum::Monat = 1;
tDatum::Jahr = Jahr;
}