C++-Programmierung/ Eigene Datentypen definieren/ Das Klassenkonzept

Aus Wikibooks


In einem späteren Abschnitt werden Sie etwas über objektorientierte Programmierung erfahren. Die notwendige Basis dafür werden wir in diesem Abschnitt erarbeiten. Sie haben inzwischen einiges über die verschiedenen Basisdatentypen gelernt und auch erfahren, wie Sie diese Daten mithilfe von Funktionen manipulieren können. Im Prinzip könnten Sie mit diesen Mitteln bereits jedes beliebige Programm schreiben. Allerdings werden Programme, die mit diesen Mitteln geschrieben wurden oft unübersichtlich, sobald eine gewisse Größe überschritten wird. Aus diesem Grund wurde ein neues Konzept erdacht, das für mehr Übersicht sorgt. Anstatt einfach Daten (Variablen) zu haben, die von Funktionen manipuliert werden, fasst man logisch zusammengehörende Daten und die darauf arbeitenden Funktionen zu einem neuen Konstrukt zusammen. Dieses Konstrukt nennt sich „Klasse“.

Eine Klasse hat üblicherweise einen Namen und besteht aus Variablen und Funktionen, welche als Klassenmember (zu deutsch etwa Mitglieder) bezeichnet werden. Ein Funktionsmember bezeichnet man auch als Methode der Klasse. Ein wesentlicher Vorteil der Verwendung von Klassen besteht darin, dass ein Nutzer der Klasse üblicherweise nicht wissen muss, wie die Klasse intern arbeitet. Andererseits kann man Klassen so gestalten, dass man von außen nur auf bestimmte klasseneigene Variablen und Funktionen zugreifen kann. Dann können Sie die interne konkrete Implementierung (abgesehen von diesen Variablen- und Funktionsnamen) der Klasse jederzeit ändern, ohne dass der Code, in dem die Klasse verwendet wird, geändert werden muss. Der Code außerhalb der Klasse muss nur dann geändert werden, wenn die Variablen- und Funktionennamen der Klasse, auf die man von außen zugreifen kann, geändert wurden. Meistens werden übrigens die Variablen einer Klasse nicht sichtbar gemacht, und man verwendet stattdessen Funktionen, um die Variablen zu verändern. Dabei kommt es durchaus häufig vor, dass eine Funktion nichts anderes tut, als eine Variable zu verändern oder sie auszulesen. Dabei spricht man auch von "Getter"- und "Setter"-Funktionen.

Eine Klasse ist ein benutzerdefinierter Datentyp. Somit können Sie also, genau wie bei einem Basisdatentyp, Variablen vom Typ der Klasse (= deren Name) erstellen. Eine Variable vom Typ einer Klasse beinhaltet alle in der Klasse deklarierten Variablen. Somit verbraucht eine Klassenvariable soviel Speicherplatz wie die Variablen, die in ihr deklariert wurden. Natürlich hat der Übersetzer hier einige Freiräume, sodass Sie nicht einfach davon ausgehen können, dass eine Klassenvariable genau soviel Speicherplatz belegt, wie die Summe ihrer Variablenmember. Wenn der Übersetzer es aus Optimierungsgründen für sinnvoll hält, etwas mehr Speicherplatz zu nutzen, um etwa die internen Variablen so im Speicher anzuordnen, dass auf sie schnell zugegriffen werden kann, darf er dies tun. Wenn Sie die genaue Größe wissen möchten, die eine Variable einer Klasse im Speicher belegt, können Sie den sizeof-Operator auf die Klasse oder eine Variable vom Typ der Klasse anwenden.

Der Inhalt einer solchen Variable ist ein „Objekt“ der Klasse. Ebenso wie es vom Typ 'int' viele Variablen mit unterschiedlichen Werten in einem Programm geben kann, so kann es viele Objekte mit unterschiedlichen Werten vom selben Klassen-Typ in einem Programm geben.

Eine Klasse ist wie ein Bauplan für Objekte. Klassen haben zwei spezielle Methoden, die beim Erstellen bzw. Zerstören eines Objektes vom Typ der Klasse aufgerufen werden. Erstere bezeichnet man als Konstruktor und die zweite als Destruktor der Klasse.

Ähnlich wie Funktionen ihre lokalen Variablen haben können, auf die „von außen“ nicht zugegriffen werden kann, gibt es innerhalb einer Klasse verschiedene Sichtbarkeitsarten für Variablen, die darüber entscheiden, ob eine Membervariable nur innerhalb der Klasse oder auch von außerhalb verwendet werden kann. Später werden wir sehen, dass man diese Sichtbarkeitsart durch spezielle Ausdrücke wie etwa public oder private festlegt.

Es gibt einige allgemeine Operatoren für Klassen und Objekte. Es ist möglich, einige dieser Operatoren für eine Klasse zu überladen, sodass diese dann auf Klassenobjekte angewendet werden können.

Das waren jetzt wieder sehr viele Informationen auf wenig Raum, aber machen Sie sich keine Sorgen, wenn Sie sich nicht alles merken konnten: In den nächsten Kapiteln wird auf die verschiedenen Eigenschaften von Klassen noch näher eingegangen, sodass Sie die Gedanken dahinter nachvollziehen können.

Ein eigener Datentyp[Bearbeiten]

Nun wird es aber Zeit, dass wir auch mal eine eigene Klasse schreiben. Das folgende Beispiel soll die prinzipielle Arbeitsweise einer Klasse demonstrieren.

#include <iostream>

class Auto{
public:
    Auto(int tankgroesse, float tankinhalt, float verbrauch);

    void info()const;

    void fahren(int km);
    void tanken(float liter);

private:
    int   tankgroesse_;
    float tankinhalt_;
    float verbrauch_;
};

Auto::Auto(int tankgroesse, float tankinhalt, float verbrauch):
    tankgroesse_(tankgroesse),
    tankinhalt_(tankinhalt),
    verbrauch_(verbrauch)
    {}

void Auto::info()const{
    std::cout << "In den Tank passen " << tankgroesse_ << " Liter Treibstoff.\n";
    std::cout << "Aktuell sind noch " << tankinhalt_ << " Liter im Tank.\n";
    std::cout << "Der Wagen verbraucht " << verbrauch_ << " Liter pro 100 km.\n";
    std::cout << std::endl;
}

void Auto::fahren(int km){
    std::cout << "Fahre " << km << " km.\n";
    tankinhalt_ -= verbrauch_*km/100;

    if(tankinhalt_ < 0.0f){
        tankinhalt_ = 0.0f;

        std::cout << "Mit dem aktuellen Tankinhalt schaffen Sie die Fahrt leider nicht.\n";
        std::cout << "Der Wagen ist unterwegs liegengeblieben, Zeit zu tanken!\n";
    }

    std::cout << std::endl;
}

void Auto::tanken( float liter ) {
    std::cout << "Tanke " << liter << " Liter.\n";
    tankinhalt_ += liter;

    if( tankinhalt_ > tankgroesse_ ) {
        tankinhalt_ = tankgroesse_;

        std::cout << "Nicht so übereifrig! Ihr Tank ist jetzt wieder voll.\n";
        std::cout << "Sie haben aber einiges daneben gegossen!\n";
    }

    std::cout << std::endl;
}

Diese Klasse nutzt vieles, was Sie im Laufe dieses Abschnittes noch kennenlernen werden. Für den Moment sollten Sie wissen, dass diese Klasse drei verschiedene Daten beinhaltet. Diese Daten sind die drei Variablen, deren Namen auf einen Unterstrich (_) enden. Vier Funktionen arbeiten auf diesen Daten.

Sie haben nun gesehen, wie die Klasse aufgebaut ist, und in den folgenden Kapiteln wird dieser Aufbau genauer erläutert. Jetzt sollen Sie jedoch erst einmal den Vorteil einer Klasse verstehen, denn um eine Klasse zu benutzen, müssen Sie keine Ahnung haben, wie diese Klasse intern funktioniert.


int main(){
    Auto wagen(80, 60.0f, 5.7f);

    wagen.info();

    wagen.tanken(12.4f);
    wagen.info();

    wagen.fahren(230);
    wagen.info();

    wagen.fahren(12200);
    wagen.info();

    wagen.tanken(99.0f);
    wagen.info();
}
Ausgabe:
In den Tank passen 80 Liter Treibstoff.
Aktuell sind noch 60 Liter im Tank.
Der Wagen verbraucht 5.7 Liter pro 100 km.

Tanke 12.4 Liter.

In den Tank passen 80 Liter Treibstoff.
Aktuell sind noch 72.4 Liter im Tank.
Der Wagen verbraucht 5.7 Liter pro 100 km.

Fahre 230 km.

In den Tank passen 80 Liter Treibstoff.
Aktuell sind noch 59.29 Liter im Tank.
Der Wagen verbraucht 5.7 Liter pro 100 km.

Fahre 12200 km.
Mit dem aktuellen Tankinhalt schaffen Sie die Fahrt leider nicht.
Der Wagen ist unterwegs liegengeblieben, Zeit zu tanken!

In den Tank passen 80 Liter Treibstoff.
Aktuell sind noch 0 Liter im Tank.
Der Wagen verbraucht 5.7 Liter pro 100 km.

Tanke 99 Liter.
Nicht so übereifrig! Ihr Tank ist jetzt wieder voll.
Sie haben aber einiges daneben gegossen!

In den Tank passen 80 Liter Treibstoff.
Aktuell sind noch 80 Liter im Tank.
Der Wagen verbraucht 5.7 Liter pro 100 km.

In der ersten Zeile von main() wird ein Auto-Objekt mit dem Namen wagen erstellt. Anschließend werden Methoden dieses Objekts aufgerufen, um die Daten darin zu verwalten. Von den Daten innerhalb des Objekts kriegen Sie beim Arbeiten mit dem Objekt überhaupt nichts mit. Lediglich die Ausgabe verrät, dass die drei Methoden untereinander über diese Daten „kommunizieren“.

Die vierte Methode (jene, die mit dem Klassennamen identisch ist) wird übrigens auch aufgerufen. Gleich in der ersten Zeile von main() wird diese Methode genutzt, um das Objekt zu erstellen. Es handelt sich also um den Konstruktor der Klasse.