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.
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.
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.
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.
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.
#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
#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
#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
#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
Bei welchen Anwendungsfällen kann es sinvoll sein, trotz fertig implementierter Unterklassen Variablen oder Methoden vom Typ der Basisklasse zu verwenden?
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?
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?
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.
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.
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