Programmieren in C++: Einführung


Kapitel 9: Klassen und Objekte


Inhaltsverzeichnis

Dieses Buch ist unter einer Creative Commons-Lizenz lizensiert.


9.1 Allgemeines

Objektorientierte Programmierung

Alles, was Sie in den vorherigen Kapiteln kennen gelernt haben, war natürlich bereits Bestandteil der Programmiersprache C++. Dennoch sieht die Entwicklung eines C++-Programms meist anders aus als das, was Sie bisher an Beispielen gesehen haben.

Bis jetzt haben Sie große Teile des Grundwortschatzes von C++ kennen gelernt und sie eher rudimentär angewandt. In der Praxis bestehen C++-Programme normalerweise vorrangig aus Klassen. Entwickler konzentrieren sich auf das Erstellen von Klassen, die an die speziellen Bedürfnisse, die die Aufgabenstellung erfordert, angepasst werden. Sind die Klassen erstellt, werden sie als Datentypen für die Definition von Variablen verwendet. Diese Variablen heißen Objekte, und mit diesen wird innerhalb eines Programms dann gearbeitet.


9.2 Objektorientierte Programmierung

Von Architekten und Häusern

Die objektorientierte Programmierung ermöglicht das Übernehmen von Objekten aus der Aufgabenstellung hinüber in die Programmentwicklung. Es wird also versucht, in der Entwicklung die gleichen Objekte zu verwenden wie sie in der konkreten Aufgabenstellung vorkommen. Dieses Übernehmen von Objekten eins zu eins aus der Aufgabenstellung in die Entwicklung bedeutet, dass keine Transferleistung zu erbringen ist und nicht zwischen Aufgabenstellung und Aufgabenlösung - also der Programmierung - abstrahiert werden muss.

Die objektorientierte Programmierung ermöglicht eine Problemlösung nahe der Aufgabenstellung. Es geht nicht darum, eine Lösung zu entwickeln, die an die Hardware und all ihre Besonderheiten angepasst werden muss. Objektorientierte Programmiersprachen lösen sich von der Hardware und orientieren sich direkt an der Aufgabenstellung.

Ein gutes objektorientiertes Programm ist daher eine Anwendung, die möglichst konkret die einzelnen Objekte, die in der Aufgabenstellung vorkommen und die zur Problemlösung miteinander agieren, herausarbeitet und diese nachbildet. Als objektorientierter Programmierer müssen Sie daher sinnvolle Objekte identifizieren und sie innerhalb einer Programmiersprache beschreiben können. Diese Beschreibung muss so umfangreich sein, dass das Objekt auch tatsächlich zur Problemlösung herangezogen werden kann. Gleichzeitig darf die Beschreibung nicht zu umfangreich sein, da sie sonst zu viele unwesentliche Elemente enthält und die Entwicklung erschwert.

Im Zusammenhang mit der Objektorientierung ist immer wieder die Rede von Klassen und Objekten. Um was handelt es sich hierbei genau?

Stellen Sie sich vor, Sie bauen ein Haus. Der erste Schritt besteht darin, dass Sie einen detaillierten Plan erstellen, der genau beschreibt, wie das Haus später aussehen soll. In diesem Plan müssen alle wesentlichen Beschreibungen des Hauses enthalten sein. Unwesentliche Merkmale müssen weggelassen werden, da dies den Plan nur unübersichtlich machen und den Hausbau erschweren würde.

Ist der Plan fertig, kann es losgehen - auf einem von Ihnen gekauften Grundstück wird nun ein Haus gebaut. Dieses Haus sieht nach der Fertigstellung natürlich genauso aus wie im Plan beschrieben.

Nachdem Sie noch etwas Kleingeld übrig haben, beschließen Sie, ein zweites Haus zu bauen. Auch für das zweite Haus wird genau der gleiche Plan verwendet wie für das erste Haus. Es sieht dementsprechend dem ersten Haus ähnlich, steht aber natürlich auf einem anderen Grundstück, vielleicht in einer anderen Straße oder sogar in einer anderen Stadt.

In der objektorientierten Terminologie entspricht der Plan der Klasse und die gebauten Häuser den Objekten. Während es mehrere Häuser geben kann, die mit Hilfe des gleichen Plans erbaut wurden, so kann es mehrere Objekte vom Typ einer Klasse geben. So wie der Plan lediglich eine Beschreibung eines Hauses ist, ist die Klasse lediglich eine Beschreibung eines Objekts. Weder der Plan alleine noch die Klasse machen jedoch das Haus bzw. das Objekt lebendig. Es handelt sich hierbei lediglich um Beschreibungen. So wie Sie erst in das fertiggebaute Haus einziehen können, können Sie in Ihrem Programm auch nur mit Objekten arbeiten. Sie benötigen zum Erstellen eines Objekts eine Klasse, die das Objekt beschreibt - so wie Sie zum Bau eines Hauses auch erstmal einen Plan benötigen.

Andere Analogien sprechen von Art oder Gattung. Wenn mehrere Objekte vom Typ einer Klasse sind, gehören sie derselben Gattung an, sind sie derselben Art. So wie es die Fahrzeugklasse 318 von BMW gibt, gibt es mehrere Fahrzeuge von dieser Fahrzeugklasse - eine Klasse, mehrere Objekte.

Die C++-Bibliothek stellt eine Menge an Klassen zur Verfügung, die sich speziell zur Verwaltung von Daten im Speicher eignen. In diesem Fall müssen Sie die Klassen nicht erst entwickeln, sondern können gleich Objekte von diesen Klassen erstellen und mit diesen Objekten Ihre Daten verwalten.

Normalerweise ist es jedoch auch immer notwendig, eigene Klassen zu erstellen. Jede Aufgabe ist anders, und während Sie teilweise auf Klassen zurückgreifen können, die von anderen Entwicklern programmiert wurden, so müssen doch fast immer auch selber Klassen entwickelt werden, die zum aktuellen Problem passen. Die Modellierung eines Ausschnitts der Wirklichkeit innerhalb der Programmierung zur gezielten Problemlösung ist das Ziel objektorientierter Sprachen.

Ähnlich wie bei Funktionen, Strukturen und Enumerationen gilt auch bei Objekten: Zuerst die Definition, dann die Verwendung. Die Definition ist hierbei die Klasse, die Verwendung das Anlegen eines Objekts. Das heißt auch, ohne Klasse kein Objekt. Klassen und Objekte gehören also untrennbar zusammen. Objekte werden durch Klassen beschrieben und können ohne diese nicht erstellt werden.


9.3 Praxis-Beispiel

Erstellen einer Klasse

Im Folgenden soll Ihnen gezeigt werden, wie eine Klasse selber erstellt wird und davon ein Objekt im Programm angelegt wird. In diesem Beispiel geht es um die Abbildung eines Rennwagens, beispielsweise für ein Computerspiel. Um das Beispiel nicht zu unübersichtlich zu gestalten, wird der Rennwagen nur sehr minimal beschrieben. Er soll lediglich eine Möglichkeit zum Beschleunigen und Abbremsen bieten - mehr nicht.

class rennwagen 
{ 
    int Kmh; 

  public: 
    rennwagen() : Kmh(0) 
    { 
    } 

    void beschleunigen(int kmh) 
    { 
      Kmh += kmh; 
    } 

    void bremsen() 
    { 
      Kmh -= 50; 
      if (Kmh < 0) 
      { 
        Kmh = 0; 
      } 
    } 
}; 

Eine Klasse wird normalerweise mit dem Schlüsselwort class definiert. Hinter class folgt der Name der Klasse, und hinter diesem zwei geschweifte Klammern, zwischen denen die Klassendefinition steht. Beachten Sie, dass hinter der geschlossenen geschweiften Klammer ein Semikolon stehen muss.

Klassen bestehen aus unterschiedlichen Merkmalen, nämlich aus Eigenschaften und Methoden. Eigenschaften sind technisch betrachtet nichts anderes als Variablen, und Methoden sind einfach nur Funktionen. Indem für Klassen also Variablen und Funktionen definiert werden, werden Objekte beschrieben.

Unser Rennwagen soll beschleunigt und abgebremst werden können. Nachdem es sich hierbei um Vorgänge handelt, werden diese als Methode und nicht als Eigenschaft abgebildet. Methoden sind grundsätzlich auch als Fähigkeiten eines Objekts bekannt.

Im obigen Beispiel sehen Sie, dass die Klasse rennwagen zwei Methoden namens beschleunigen() und bremsen() definiert. Zusätzlich ist eine dritte Methode definiert, die genauso heißt wie die Klasse selber: rennwagen(). Es handelt sich hierbei um eine spezielle Methode, die Konstruktor genannt wird, und verwendet wird, um ein Objekt zu initialisieren. In diesem Fall wird eine Eigenschaft Kmh der Klasse rennwagen auf den Wert 0 gesetzt. Das bedeutet, dass beim Erstellen eines Objekts vom Typ rennwagen die Anfangsgeschwindigkeit 0 ist und der Rennwagen steht. Die Eigenschaft Kmh ist selber einfach als Variable innerhalb der Klasse angelegt.

Anhand der Eigenschaft Kmh wird nun das Objekt beschleunigt oder abgebremst. Die Methode beschleunigen() erwartet einen Parameter vom Typ int und erhöht die Eigenschaft Kmh um diesen entsprechend. Die Methode bremsen() verringert den Wert der Eigenschaft hingegeben um 50. Außerdem wird überprüft, ob 0 unterschritten wurde. In diesem Fall wird die Eigenschaft Kmh auf 0 gesetzt, um eine negative Geschwindigkeit zu verhindern.

Eine derartige Klassendefinition wird für gewöhnlich in eine Header-Datei gelegt, die normalerweise auch genauso genannt wird wie die Klasse selber. Obige Klasse könnte also in einer Datei namens rennwagen.h gespeichert werden.

Sehen Sie sich nun folgendes Programm an, das die Klasse rennwagen verwendet und ein Objekt von dieser erstellt.

#include <iostream> 
#include "rennwagen.h" 

int main() 
{ 
  rennwagen MeinRennwagen; 

  MeinRennwagen.beschleunigen(250); 
  std::cout << "Rennwagen auf 250 km/h beschleunigt" << std::endl; 
  MeinRennwagen.bremsen(); 
  MeinRennwagen.bremsen(); 
  std::cout << "Rennwagen auf 150 km/h abgebremst" << std::endl; 
  MeinRennwagen.beschleunigen(50); 
  std::cout << "Rennwagen auf 200 km/h beschleunigt" << std::endl; 
  MeinRennwagen.bremsen(); 
  MeinRennwagen.bremsen(); 
  MeinRennwagen.bremsen(); 
  MeinRennwagen.bremsen(); 
  std::cout << "Rennwagen angehalten" << std::endl; 
} 

Um ein Objekt anzulegen, definieren Sie einfach eine Variable vom Typ einer Klasse. In diesem Fall wird also ein Objekt namens MeinRennwagen angelegt, das vom Typ rennwagen ist. Um den Datentyp rennwagen einsetzen zu können, müssen Sie diesen über den Präprozessor-Befehl #include bekanntmachen und die entsprechende Header-Datei mit der Klassendefinition einbinden.

Die Klasse sieht so aus, dass sie zwei Methoden beschleunigen() und bremsen() zur Verfügung stellt. Um mit Ihrem Rennwagen loszufahren, rufen Sie einfach die Methode beschleunigen() auf und übergeben einen Wert, um den der Rennwagen beschleunigt werden soll - so ist diese Methode ja definiert.

Um eine Methode aufzurufen, geben Sie zuerst das Objekt an, für das Sie die Methode aufrufen wollen - in diesem Fall also MeinRennwagen. Danach setzen Sie den Zugriffsoperator ., den Sie bereits von Strukturen her kennen. Hinter dem Punkt geben Sie nun die Methode an, die Sie aufrufen möchten. Der Methodenaufruf erfolgt so wie Sie es von Funktionsaufrufen gewohnt sind: Sie geben zuerst den Namen der Methode an und dahinter die runden Klammern mit möglicher Parameterliste. Nachdem die Methode beschleunigen() einen Parameter vom Typ int erwartet, wird der Rennwagen im Beispiel erstmal um 250 km/h beschleunigt. Dies wird auch hinter dem Methodenaufruf zur Kontrolle auf den Bildschirm ausgegeben.

Als nächstes wird innerhalb des Programms die Methode bremsen() aufgerufen - wiederum unter Angabe des Objektnamens und des Zugriffsoperators. Auf diese Weise wird der Rennwagen um 50 km/h abgebremst. Indem die Methoden einfach mehrmals hintereinander aufgerufen werden, kann der Rennwagen auf beliebige Geschwindigkeiten beschleunigt oder abgebremst werden.

Indem Sie nun also die Klasse rennwagen definiert haben, können Sie tatsächlich einen Rennwagen in Ihrer Programmentwicklung verwenden und ihn sogar - so wie Sie es aus der Formel 1 kennen - beschleunigen und abbremsen. Die Entwicklung des Programms orientiert sich nicht an irgendwelchen Hardware-Gegebenheiten, sondern an den Objekten, die Sie für die Problemlösung benötigen.