Übungsaufgaben zu Vererbung

Im vorangegangenen Abschnitt "Vererbung" wurde Ihnen nähergebracht, wie eine Vererbungsstruktur in C++ realisiert werden kann. Zusammenfassend lässt sich sagen, dass eine von einer Basisklasse erbende Unterklasse sämtliche Methoden und Instanzvariablen jener Basisklasse übernimmt und um eigene Definitionen ergänzt. Der Zugriffsspezifizierer (protected oder public) bestimmt, auf welche Instanzvariablen der Basisklasse von der Unterklasse aus zugegriffen werden kann.

In diesem Kontext können Methoden mit gleichem Namen auftreten, was auf die Mechanismen Vererbung (in Unterklasse), Polymorphie (in unterschiedlichen Klassen) oder Überladung (unterschiedliche Argumente) zurückgeführt werden kann. Es wurde genauer auf die Überladung von Operatoren eingegangen, wobei es einige Besonderheiten, auch in Bezug auf Jupyter Notebooks, zu beachten gibt.

Praktische Aufgabe

Hier soll nun das Zugbeispiel aus dem vorangegangen Kapitel zu Klassen fortgeführt werden. Wie bereits im einführenden Beispiel angedeutet, kann die Train-Klasse durch die Einführung von Unterklassen für bestimmte Typen von Zügen weiter diversifiziert werden. Konkret bietet sich hier die Unterteilung in Personenzüge PassengerTrain und Güterzüge FreightTrainan, die über jeweils passende Instanzvariablen und entsprechende Methoden verfügen.

Unterklassen

Implementieren Sie die Klasse PassengerTrain, die von Train auf die übliche Weise erbt. Damit nur diese Klasse über die Instanzvariable p_iPassengerCounter verfügt, kopieren Sie deren Implementierung von der Ober- in die Unterklasse und löschen sie aus Train. Gleiches gilt für die zugehörige Methode fStation.

Die Klasse FreightTrain wiederum soll ebenfalls von Train erben, aber die Instanzvariablen p_fChargeQuantity und p_sChargeType beinhalten. Standardmäßig sollen diese mit 0.0 bzw. "empty" initialisiert werden. Damit der Datentyp String verwendet werden kann, müssen Sie diesen mit #include <string> explizit einbinden.

Überschreiben Sie in den Unterklassen die Methode vPrintProperties() von Train, in der die jeweils hinzugekommenen Instanzvariablen ausgegeben werden. Um Redundanzen bei der Ausgabe der gemeinsamen Instanzvariablen (also derjenigen in der Oberklasse Train) zu vermeiden, wird als erste Operation in den Ausgabemethoden der Unterklasse Train::vPrintProperties()aufgerufen.

Erstellen Sie auch für beide Unterklassen die parametrisierten Konstruktoren, erzeugen damit jeweils einen FreightTrain sowie PassengerTrain und testen die Ausgabemethoden.

zum Lösungsvorschlag

Klassenspezifische Methoden

Die Verzögerung pro Zug soll nun gespeichert werden, anstatt nur von der Methode fStation() zurückgegeben zu werden. Legen Sie dafür eine neue Instanzvariable p_fDelay in Train an, die mit 0 initialisiert wird. Überlegen Sie sich unter Berücksichtigung des folgenden Absatzes, welcher Zugriffsspezifizierer für p_fDelay erforderlich ist.

Verrechnen Sie in der Methode PassengerTrain::fStation den aktuellen Wert von p_fDelay mit der Umsteigezeit und schreiben Sie diesen in p_fDelay, wobei die Verspätung allerdings niemals kleiner als 0 werden kann. Zurückgegeben wird weiterhin die berechnete Umsteigezeit.

Eine ähnliche Methode soll für die Klasse FreightTrain implementiert werden, die als Argument die Menge der auf- bzw. abzuladenden Güter als float übergeben bekommt. Falls nicht mehr abgeladen werden soll, als der Zug laut p_fChargeQuantity geladen hat, wird die aktualisierte Gütermenge in dieser Instanzvariable gespeichert. Geben Sie abschließend die ausgeführte Operation (auf- oder abladen, Menge, Typ) in Textform aus.

zum Lösungsvorschlag

Operatorüberladung

Schließlich soll das Konzept der Operatorüberladung auf den Ausgabeoperator der beiden Unterklassen angewandt werden, wobei deren jeweilige Instanzvariablen ausgegeben werden.

Durch die Überladung wird nun folgendes Konstrukt zur Ausgabe der Daten eines Zuges möglich: std::cout << aPassengerTrain << std::endl, also ohne direkten Aufruf der vPrintProperties() Methode. Da Sie diese allerdings schon implementiert haben, bietet es sich an, eben jene Methode im überladenen Operator aufzurufen.

Darüberhinaus kann die Methode Train::vPrintProperties(...) als virtual deklariert werden, um sicherzustellen, dass von Train erbende Klassen diese überschreiben. Wie auch die Ausgabemethoden in den Unterklassen muss ihr ebenfalls ein Parameter vom Typ std::ostream& hinzugefügt werden, dessen Argument beim Aufruf mittels << stets std::ostream ist.

Passen Sie abschließend die Ausgabe in main.cpp entsprechend an.

zum Lösungsvorschlag

#include <iostream>
//Train.h
//hier: Version aus vorheriger Lektion aktualisieren
 class Train
{
    private:     
        int p_iPassengerCounter = 0;
    
        static inline int p_iIDCounter = 0;
        const int p_iID = p_iIDCounter++;
    public:
        Train() = default;
        Train(int passenger);
        virtual ~Train() = default;
        
        void vPrintProperties() const;
        float station(int passengersIn, int passengersOut);
}
//Train.cpp
//hier: Version aus vorheriger Lektion aktualisieren
Train::Train(int passenger): p_iPassengerCounter(passenger)
{
    std::cout << "Aufruf des Nicht-Standardkonstruktors" << std::endl;
}
//Train.cpp
//hier: Ausgabemethode aus vorheriger Lektion aktualisieren
void Train::vPrintProperties() const
{
    std::cout << "ID: " << p_iID << std::endl;
    
    std::cout << "Anzahl Passagiere: " << p_iPassengerCounter << std::endl;
}
//PassengerTrain.h
//hier: Klasse PassengerTrain implementieren
//PassengerTrain.cpp
//hier: Konstruktor für PassengerTrain implementieren
//PassengerTrain.cpp
//hier: Ausgabemethode implementieren
//PassengerTrain.cpp
//hier: fStation(...) implementieren
//FreightTrain.h
//hier: Klasse FreightTrain implementieren
//FreightTrain.cpp
//hier: Konstruktor für FreightTrain implementieren
//FreightTrain.cpp
//hier: Ausgabemethode implementieren
//FreightTrain.cpp
//hier: vLoadCharge(...) implementieren
//main.cpp
//hier: Ausgabeoperator überladen
#define OPERATOR operator<<

#undef OPERATOR 
//main.cpp 
//hier: Instanzen erzeugen, Methoden aufrufen und Ausgabe testen

Lösungsvorschlag zu 1.

zur Aufgabenstellung

#include <iostream>
#include <string>
//Train.h 
class Train
{
    private:
        static inline int p_iIDCounter = 0;
        const int p_iID = p_iIDCounter++;
    
    public:
        Train() = default;
        Train(int speed, bool electric);
        virtual ~Train() = default;
        
        void vPrintProperties() const;
}
//Train.cpp
void Train::vPrintProperties() const
{
    std::cout << "ID: " << p_iID << std::endl;
    
    //ausgabe << "Anzahl Passagiere: " << p_iPassengerCounter << std::endl; -> jetzt in PassengerTrain
}
//PassengerTrain.h
class PassengerTrain : public Train
{
    public:
        PassengerTrain() = default;
        PassengerTrain(int p_iPassengerCounter);
        virtual ~PassengerTrain() = default;
        
        void vPrintProperties() const;
        float fStation(int passengersIn, int passengersOut);
    
    private:
        int p_iPassengerCounter = 0;

}
//PassengerTrain.cpp
PassengerTrain::PassengerTrain(int passengers) : p_iPassengerCounter(passengers)
{
    std::cout << "Aufruf des Nicht-Standardkonstruktors" << std::endl;
}
//PassengerTrain.cpp
void PassengerTrain::vPrintProperties() const
{
    Train::vPrintProperties();
    std::cout << "Anzahl Passagiere: " << p_iPassengerCounter << std::endl;
}
//PassengerTrain.cpp
// gibt Änderung der Verspätung zurück und Verändert `delay` (aber delay >= 0)

/* Annahmen / Funktion
bei Haltestelle: 2 Minuten Zeit. 
    Pro einsteigender Passagier: 10 Sekunden
    Pro aussteigender Passagier: 5 Sekunden
    nicht parallel
alles >2 Minuten: neue Verspätung
*/

float PassengerTrain::fStation(int passengersIn, int passengersOut)
{    
    // mehr Passagiere als Aussteigende? -> fehlerhafte Einhabe -> mache nichts
    if (p_iPassengerCounter < passengersOut)
    {
        std::cout << "Es koennen nicht mehr Personen aussteigen als sich im Zug befinden" << std::endl;
        return 0;
    }
    
    p_iPassengerCounter = p_iPassengerCounter + passengersIn - passengersOut;
    
    
    // Rechnung in Sekunden, Umrechnung in Minuten spaeter    
    float secondsChange = 120 - passengersIn * 10 - passengersOut * 5;    
    return secondsChange/60;
}
//FreightTrain.h
class FreightTrain : public Train
{
    private:
        float p_fChargeQuantity = 0.0;
        std::string p_sChargeType = "default";
        
    public:
        FreightTrain() = default;
        FreightTrain(float chargeQuantity, std::string chargeType);
        virtual ~FreightTrain() = default;
        
        void vPrintProperties() const;
}
//FreightTrain.cpp
FreightTrain::FreightTrain(float chargeQuantity, std::string chargeType): p_fChargeQuantity(chargeQuantity), p_sChargeType(chargeType)
{}
//FreightTrain.cpp
void FreightTrain::vPrintProperties() const
{
    Train::vPrintProperties();
    std::cout << "Ladung: " << p_fChargeQuantity << " " << p_sChargeType << std::endl;
}
//main.cpp
PassengerTrain aTrain = PassengerTrain(30);
FreightTrain bTrain = FreightTrain(2.0, "Kisten Schokolade");

std::cout << std::endl << "Eigenschaften 'aTrain':" << std::endl;
aTrain.vPrintProperties();

for(int i = 0; i < 3; i++)
{
    std::cout << std::endl << "Eigenschaften 'aTrain' nach "<< i+1 << ". Aufruf von Station: " << std::endl;
    aTrain.fStation(3, 10);
    aTrain.vPrintProperties();
}
Aufruf des Nicht-Standardkonstruktors

Eigenschaften 'aTrain':
ID: 0
Anzahl Passagiere: 30

Eigenschaften 'aTrain' nach 1. Aufruf von Station: 
ID: 0
Anzahl Passagiere: 23

Eigenschaften 'aTrain' nach 2. Aufruf von Station: 
ID: 0
Anzahl Passagiere: 16

Eigenschaften 'aTrain' nach 3. Aufruf von Station: 
ID: 0
Anzahl Passagiere: 9

Lösungsvorschlag zu 2.

zur Aufgabenstellung

#include <iostream>
#include <string>
//Train.h 
class Train
{
    private:  
        static inline int p_iIDCounter = 0;
        const int p_iID = p_iIDCounter++;
    
    protected:
        float p_fDelay = 0;
    
    public:
        Train() = default;
        Train(int speed, bool electric);
        virtual ~Train() = default;
        
        void vPrintProperties() const;
}
//Train.cpp
void Train::vPrintProperties() const
{
    std::cout << "ID: " << p_iID << std::endl;
  
    //ausgabe << "Anzahl Passagiere: " << p_iPassengerCounter << std::endl; -> jetzt in PassengerTrain
    std::cout << "momentane Verspätung: " << p_fDelay << " Minuten"<< std::endl;
}
//PassengerTrain.h
class PassengerTrain : public Train
{
    public:
        PassengerTrain() = default;
        PassengerTrain(int p_iPassengerCounter);
        virtual ~PassengerTrain() = default;
        
        void vPrintProperties() const;
        float fStation(int passengersIn, int passengersOut);
    
    private:
        int p_iPassengerCounter = 0;

}
//PassengerTrain.cpp
PassengerTrain::PassengerTrain(int passengers) : p_iPassengerCounter(passengers)
{
    std::cout << "Aufruf des Nicht-Standardkonstruktors" << std::endl;
}
//PassengerTrain.cpp
void PassengerTrain::vPrintProperties() const
{
    Train::vPrintProperties();
    std::cout << "Anzahl Passagiere: " << p_iPassengerCounter << std::endl;
}
//PassengerTrain.cpp
// gibt Änderung der Verspätung zurück und Verändert `delay` (aber delay >= 0)

/* Annahmen / Funktion
bei Haltestelle: 2 Minuten Zeit. 
    Pro einsteigender Passagier: 10 Sekunden
    Pro aussteigender Passagier: 5 Sekunden
    nicht parallel
alles >2 Minuten: neue Verspätung
*/

float PassengerTrain::fStation(int passengersIn, int passengersOut)
{    
    // mehr Passagiere als Aussteigende? -> fehlerhafte Einhabe -> mache nichts
    if (p_iPassengerCounter < passengersOut)
    {
        std::cout << "Es koennen nicht mehr Personen aussteigen als sich im Zug befinden" << std::endl;
        return 0;
    }
    
    p_iPassengerCounter = p_iPassengerCounter + passengersIn - passengersOut;
    
    
    // Rechnung in Sekunden, Umrechnung in Minuten spaeter    
    float secondsChange = 120 - passengersIn * 10 - passengersOut * 5;
    p_fDelay = (p_fDelay - secondsChange < 0) ? 0 : (p_fDelay - secondsChange)/60;
    
    return secondsChange/60;
}
//FreightTrain.h
class FreightTrain : public Train
{
    private:
        float p_fChargeQuantity = 0.0;
        std::string p_sChargeType = "default";
        
    public:
        FreightTrain() = default;
        FreightTrain(float chargeQuantity, std::string chargeType);
        virtual ~FreightTrain() = default;
        
        void vPrintProperties() const;
        void vLoadCharge(float amount);
}
//FreightTrain.cpp
FreightTrain::FreightTrain(float chargeQuantity, std::string chargeType) : p_fChargeQuantity(chargeQuantity), p_sChargeType(chargeType)
{}
//FreightTrain.cpp
void FreightTrain::vPrintProperties() const
{
    Train::vPrintProperties();
    std::cout << "Ladung: " << p_fChargeQuantity << " " << p_sChargeType << std::endl;
}
//FreightTrain.cpp
// abladen: amount < 0
void FreightTrain::vLoadCharge(float amount)
{ 
    if (p_fChargeQuantity + amount < 0)
    {
        std::cout << "Es kann nicht mehr abgeladen werden als geladen ist. Abbruch." << std::endl;
        return;
    }
    p_fChargeQuantity += amount;
    std::cout << "Es wurden " << amount << " " << p_sChargeType;
    
    if (amount > 0) std::cout << " aufgeladen.";
    else std::cout << " abgeladen.";
}
//main.cpp
PassengerTrain aTrain = PassengerTrain(30);
FreightTrain bTrain = FreightTrain(2.0, "Kisten Schokolade");

std::cout << std::endl << "Eigenschaften 'aTrain':" << std::endl;
aTrain.vPrintProperties();

for(int i = 0; i < 3; i++)
{
    std::cout << std::endl << "Eigenschaften 'aTrain' nach "<< i+1 << ". Aufruf von Station: " << std::endl;
    aTrain.fStation(13, 20);
    aTrain.vPrintProperties();
}

std::cout << std::endl << "Eigenschaften 'bTrain':" << std::endl;
bTrain.vPrintProperties(); 

std::cout << std::endl << "\n\n\nEigenschaften 'bTrain' nach 1. load:" << std::endl;
bTrain.vLoadCharge(-10);
bTrain.vPrintProperties();

std::cout << std::endl << "\n\n\nEigenschaften 'bTrain' nach 2. load:" << std::endl;
bTrain.vLoadCharge(3.3);
bTrain.vPrintProperties();
Aufruf des Nicht-Standardkonstruktors

Eigenschaften 'aTrain':
ID: 0
momentane Verspätung: 0 Minuten
Anzahl Passagiere: 30

Eigenschaften 'aTrain' nach 1. Aufruf von Station: 
ID: 0
momentane Verspätung: 1.83333 Minuten
Anzahl Passagiere: 23

Eigenschaften 'aTrain' nach 2. Aufruf von Station: 
ID: 0
momentane Verspätung: 1.86389 Minuten
Anzahl Passagiere: 16

Eigenschaften 'aTrain' nach 3. Aufruf von Station: 
Es koennen nicht mehr Personen aussteigen als sich im Zug befinden
ID: 0
momentane Verspätung: 1.86389 Minuten
Anzahl Passagiere: 16

Eigenschaften 'bTrain':
ID: 1
momentane Verspätung: 0 Minuten
Ladung: 2 Kisten Schokolade




Eigenschaften 'bTrain' nach 1. load:
Es kann nicht mehr abgeladen werden als geladen ist. Abbruch.
ID: 1
momentane Verspätung: 0 Minuten
Ladung: 2 Kisten Schokolade




Eigenschaften 'bTrain' nach 2. load:
Es wurden 3.3 Kisten Schokolade aufgeladen.ID: 1
momentane Verspätung: 0 Minuten
Ladung: 5.3 Kisten Schokolade

Lösungsvorschlag zu 3.

zur Aufgabenstellung

#include <iostream>
#include <string>
//Train.h 
class Train
{
    private:   
        static inline int p_iIDCounter = 0;
        const int p_iID = p_iIDCounter++;
    
    protected:
        float p_fDelay = 0;
    
    public:
        Train() = default;
        Train(int speed, bool electric);
        virtual ~Train() = default;
        
        virtual void vPrintProperties(std::ostream& ausgabe) const;
}
//Train.cpp
void Train::vPrintProperties(std::ostream& ausgabe) const
{
    ausgabe << "ID: " << p_iID << std::endl;
    
    //ausgabe << "Anzahl Passagiere: " << p_iPassengerCounter << std::endl; -> jetzt in PassengerTrain
    ausgabe << "momentane Verspätung: " << p_fDelay << " Minuten"<< std::endl;
}
//PassengerTrain.h
class PassengerTrain : public Train
{
    public:
        PassengerTrain() = default;
        PassengerTrain(int p_iPassengerCounter);
        virtual ~PassengerTrain() = default;
        
        void vPrintProperties(std::ostream& ausgabe) const;
        float fStation(int passengersIn, int passengersOut);
    
    private:
        int p_iPassengerCounter = 0;

}
//PassengerTrain.cpp
PassengerTrain::PassengerTrain(int passengers) : p_iPassengerCounter(passengers)
{
    std::cout << "Aufruf des Nicht-Standardkonstruktors" << std::endl;
}
//PassengerTrain.cpp
void PassengerTrain::vPrintProperties(std::ostream& ausgabe) const
{
    Train::vPrintProperties(ausgabe);
    ausgabe << "Anzahl Passagiere: " << p_iPassengerCounter << std::endl;
}
//PassengerTrain.cpp
// gibt Änderung der Verspätung zurück und Verändert `delay` (aber delay >= 0)

/* Annahmen / Funktion
bei Haltestelle: 2 Minuten Zeit. 
    Pro einsteigender Passagier: 10 Sekunden
    Pro aussteigender Passagier: 5 Sekunden
    nicht parallel
alles >2 Minuten: neue Verspätung
*/

float PassengerTrain::fStation(int passengersIn, int passengersOut)
{    
    // mehr Passagiere als Aussteigende? -> fehlerhafte Einhabe -> mache nichts
    if (p_iPassengerCounter < passengersOut)
    {
        std::cout << "Es koennen nicht mehr Personen aussteigen als sich im Zug befinden" << std::endl;
        return 0;
    }
    
    p_iPassengerCounter = p_iPassengerCounter + passengersIn - passengersOut;
    
    
    // Rechnung in Sekunden, Umrechnung in Minuten spaeter    
    float secondsChange = 120 - passengersIn * 10 - passengersOut * 5;
    p_fDelay = (p_fDelay - secondsChange < 0) ? 0 : (p_fDelay - secondsChange)/60;
    
    return secondsChange/60;
}
//FreightTrain.h
class FreightTrain : public Train
{
    private:
        float p_fChargeQuantity = 0.0;
        std::string p_sChargeType = "default";
        
    public:
        FreightTrain() = default;
        FreightTrain(float chargeQuantity, std::string chargeType);
        virtual ~FreightTrain() = default;
        
        void vPrintProperties(std::ostream& ausgabe) const;
        void vLoadCharge(float amount);
}
//FreightTrain.cpp
FreightTrain::FreightTrain(float chargeQuantity, std::string chargeType) : p_fChargeQuantity(chargeQuantity), p_sChargeType(chargeType)
{}
//FreightTrain.cpp
void FreightTrain::vPrintProperties(std::ostream& ausgabe) const
{
    Train::vPrintProperties(ausgabe);
    ausgabe << "Ladung: " << p_fChargeQuantity << " " << p_sChargeType << std::endl;
}
//FreightTrain.cpp
// abladen: amount < 0
void FreightTrain::vLoadCharge(float amount)
{ 
    if (p_fChargeQuantity + amount < 0)
    {
        std::cout << "Es kann nicht mehr abgeladen werden als geladen ist. Abbruch." << std::endl;
        return;
    }
    p_fChargeQuantity += amount;
    std::cout << "Es wurden " << amount << " " << p_sChargeType;
    
    if (amount > 0) std::cout << " aufgeladen.";
    else std::cout << " abgeladen.";
}
#define OPERATOR operator<<

std::ostream & OPERATOR(std::ostream& out, const Train& train)
{
    train.vPrintProperties(out);
    return out;
}

#undef OPERATOR 

Hinweis: Der Block oben sollte eigentlich so aussehen:

std::ostream & operator<<(std::ostream& out, const Train& train)
{
    train.vPrintProperties(out);
    return out;
}

Der Rest ist nur in Jupyter notwendig

//main.cpp
PassengerTrain aTrain = PassengerTrain(30);
FreightTrain bTrain = FreightTrain(2.0, "Kisten Schokolade");

std::cout << std::endl << "Eigenschaften 'aTrain':" << std::endl;
std::cout << aTrain;

for(int i = 0; i < 3; i++)
{
    std::cout << std::endl << "Eigenschaften 'aTrain' nach "<< i+1 << ". Aufruf von Station: " << std::endl;
    aTrain.fStation(13, 20);
    std::cout << aTrain;
}

std::cout << std::endl << "Eigenschaften 'bTrain':" << std::endl;
std::cout << bTrain;

std::cout << std::endl << "\n\n\nEigenschaften 'bTrain' nach 1. load:" << std::endl;
bTrain.vLoadCharge(-10);
std::cout << bTrain;

std::cout << std::endl << "\n\n\nEigenschaften 'bTrain' nach 2. load:" << std::endl;
bTrain.vLoadCharge(3.3);
std::cout << bTrain;
Aufruf des Nicht-Standardkonstruktors

Eigenschaften 'aTrain':
ID: 0
momentane Verspätung: 0 Minuten
Anzahl Passagiere: 30

Eigenschaften 'aTrain' nach 1. Aufruf von Station: 
ID: 0
momentane Verspätung: 1.83333 Minuten
Anzahl Passagiere: 23

Eigenschaften 'aTrain' nach 2. Aufruf von Station: 
ID: 0
momentane Verspätung: 1.86389 Minuten
Anzahl Passagiere: 16

Eigenschaften 'aTrain' nach 3. Aufruf von Station: 
Es koennen nicht mehr Personen aussteigen als sich im Zug befinden
ID: 0
momentane Verspätung: 1.86389 Minuten
Anzahl Passagiere: 16

Eigenschaften 'bTrain':
ID: 1
momentane Verspätung: 0 Minuten
Ladung: 2 Kisten Schokolade




Eigenschaften 'bTrain' nach 1. load:
Es kann nicht mehr abgeladen werden als geladen ist. Abbruch.
ID: 1
momentane Verspätung: 0 Minuten
Ladung: 2 Kisten Schokolade




Eigenschaften 'bTrain' nach 2. load:
Es wurden 3.3 Kisten Schokolade aufgeladen.ID: 1
momentane Verspätung: 0 Minuten
Ladung: 5.3 Kisten Schokolade

Verständnisfragen

Unterklassen

Bei welchen Anwendungsfällen kann es sinvoll sein, trotz fertig implementierter Unterklassen Variablen oder Methoden vom Typ der Basisklasse zu verwenden?

Zugriffsspezifizierer

Warum muss für p_iPassengerCounter ein anderer Zugriffsspezifizierer als für die restlichen Instanzvariablen von Train verwendet werden? Welche anderen Möglichkeiten gäbe es, um die gewünschte Funktionalität zu erreichen?

Operatorüberladung

Einen Operator zu überladen, ist nicht nur beim Programmieren eigener Klassen nützlich, sondern wird auch in einigen integrierten Strukturen von C bzw. C++ angewandt. Welche Situationen dafür fallen Ihnen ein?


Lösungsvorschläge zu den Verständnisfragen

1. Unterklassen

Der am einfachsten nachzuvollziehende Fall läge sicherlich vor, wenn sich der Endanwender nicht für eine der Unterklassen entscheiden will. Hier könnte das beispielsweise bei einem System zur Simulation der Schienenauslastung zutreffen, falls es für die Belegung eines Schienenwegs der Typ des Zugs unerheblich ist.

Es kommt außerdem häufig vor, dass allgemeinere Datenstrukturen notwendig sind, um die Instanzen der verschiedenen Unterklassen verwalten zu können.

Vorstellbar wäre im vorliegenden Fall beispielsweise eine Liste von Güter- und Personenzügen, die von einer Bahnbetriebsgesellschaft geführt wird. Damit sie beide Typen von Zügen enthalten kann, müsste die Liste vom Typ Train sein. Um dann alle Elemente dieser Liste automatisiert ausgeben zu können, würde ein Aufruf von vPrintProperties() beziehungsweise des Ausgabeoperators ausreichen. Dass diese Operationen auch von den Unterklassen implementiert sind, wird durch das Keyword virtual bei der Methode in der Basisklasse Train sichergestellt wird. Die Erstellung und Verwaltung von Listen wird in Lektion 5 (Containerklassen) genauer erläutert.

2. Zugriffsspezifizierer

p_iPassengerCounter muss protected sein, damit in PassengerTrain::fStation darauf lesend und schreibend zugegriffen werden kann.

Alternativ könnte diese Methode in Train als friend deklariert werden, sodass sie auch auf private Attribute zugreifen kann. Eine weitere Möglichkeit bestünde in der Definition von public Getter- und Setter-Methoden, die dann in PassengerTrain::fStation aufgerufen werden müssten.

3. Operatorüberladung

Wie die meisten anderen Programmiersprachen unterscheidet auch C++ für den Endanwender auf den ersten Blick nicht zwischen mathematischen Operationen auf Zahlen vom Datentyp int, float oder Mischungen davon. Zu beachten ist allerdings, dass bei letzterem Fall, also bspw. der Addition von int und float Werten, eine Typumwandlung stattfindet. Konkret würde hier der float in eine Ganzzahlt konvertiert werden, indem lediglich die Stellen vor dem Komma berücksichtigt werden. Auf diese Weise können unter Umständen unerwünschte Effekte auftreten, da die Genauigkeit des Ergebnisses abnehmen kann.

Eine weitere Überladung liegt beim Ausgabeoperator vor, den Sie auch in dieser Lektion sowohl zum Ausgeben von String