Die Klasse: Datenstruktur mit Funktion
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;
}