Grundprinzip der Mehrfach-Ableitung
Konstruktor und Destruktor
Gleichnamige Member in den Basisklassen
Beispiel
Bisher wurde eine Klasse immer nur von einer Klasse direkt abgeleitet. C++ bietet aber auch die Möglichkeit, eine Klasse von mehreren Basisklassen abzuleiten. Die Anzahl der Basisklassen ist nicht begrenzt, jedoch sollten Sie immer versuchen ein Klasse von so wenig wie möglichen Basisklassen abzuleiten. Ansonsten geht leicht der Überblick verloren. Unten ist anhand eines kleinen Bildes das Prinzip der mehrfachen Ableitung dargestellt. Die Klasse CMixed vereint zunächst alle Eigenschaften/Memberfunktionen der Klassen CBase1 und CBase2 und fügt eventuell eigene hinzu.

Die Ableitung einer Klasse von mehreren Basisklassen erfolgt analog dem Ableiten von einer Basisklasse, nur werden jetzt bei der Definition der abgeleiteten Klasse mehrere Basisklassen, einschließlich deren Zugriffsrechte, angegeben. Die einzelnen Basisklassen werden dabei durch Komma voneinander getrennt.
// 1. Basisklasse class CBase1 {....}; // 2. Basisklasse class CBase2 {....}; // Neue abgeleitete Klasse class CMixed: public CBase1, protected CBase2 {....} |
Außer der Klassendefinition muss nun selbstverständlich auch der Konstruktor der abgeleiteten Klasse entsprechend angepasst werden. Er muss jetzt alle Konstruktore seiner Basisklassen aufrufen. Dazu wird, wie bei der einfachen Ableitung, nach der Parameterklammer zunächst ein Doppelpunkt angegeben und danach werden alle Konstruktore der Basisklassen aufgelistet. Einzige Ausnahme: Eine Basisklasse besitzt einen Standard-Konstruktor (parameterloser Konstruktor). In diesem Fall erfolgt der Aufruf automatisch.
// ctor der abgeleiteten Klasse
CMixed::CMixed(P1,P2,...):
CBase1(a1,a2,...),
CBase2(b1,b2,...)
{
....
}
|
Die Konstruktore der Basisklassen werden in der Reihenfolge abgearbeitet, in der die Basisklassen bei der Definition der abgeleiteten Klasse aufgeführt wurden. Die Reihenfolge der Konstruktoraufrufe bei der Definition des Konstruktors der abgeleiteten Klasse spielt keine Rolle, sie muss nicht einmal zwingend mit der Reihenfolge bei der Deklaration übereinstimmen. Im Beispiel unten wird also zuerst der Konstruktor der Klasse CBase1 ausgeführt und danach der Konstruktor der Klasse CBase2. Allerdings sollte bei sauberer Programmierung diese Reihenfolge keine Rolle spielen dürfen, da beide Basisklassen voneinander unabhängig sind.
// Definition der abgeleiteten Klasse class CMixed: public CBase1, protected CBase2 {....} // Definition des ctors CMixed::CMixed(P1,P2,...): CBase2(a1,a2,...), CBase1(b1,b2,...) { .... } |
Und auch für die Abarbeitung der Destruktoren gilt das Gleiche wie bei einfach abgeleiteten Klassen: die Destruktoren werden in umgekehrter Reihenfolge ausgeführt wie die Konstruktore.
Etwas acht geben müssen Sie, wenn die Basisklassen untereinander Memberfunktionen mit gleichen Namen enthalten oder sogar die abgeleitete Klasse eine Memberfunktion mit gleichem Namen besitzt wie die Basisklassen. In diesem Fall müssen Sie wieder den Klassennamen vor dem Aufruf der Memberfunktion stellen. Dies gilt sogar auch dann, wenn die Memberfunktionen unterschiedliche Parameter besitzen. Wie Sie bestimmt noch aus der vorherigen Lektion wissen, funktioniert die Sache mit dem Überladen von Memberfunktionen nur innerhalb einer Klasse und nicht über Klassengrenzen hinweg! Vergessen Sie einmal die Klasse explizit anzugeben, so meldet Ihnen der Compiler hier einen Fehler.
// 1. Basisklasse class CBase1 { .... void DoAnything(...); }; // 2. Basisklasse class CBase2 { .... void DoAnything(...); }; // Neue abgeleitete Klasse class CMixed: public CBase1, protected CBase2 { void AnyMeth(...) { CBase2::DoAnything(); } .... } |
Ja und das war's auch schon zu mehrfach abgeleiteten Klassen.
|
|
Text : ColorString Text : ColorString
modifiziert |
// Beispiel zu mehrfach abgeleiteten Klassen // Zuerst Dateien einbinden #include <iostream> #include <iomanip> #include <string> using std::cout; using std::endl; using std::string; // Basisklasse für die Aufnahme der Grafikposition class GBase { int xPos, yPos; // Grafikposition public: GBase(int x, int y): xPos(x), yPos(y) {} // Position umsetzen void SetPos(int x, int y) { xPos = x; yPos = y; } // Position zurückgeben // Ist const-Memberfunktion! void GetPos(int& x, int& y) const { x = xPos; y = yPos; } }; // Zusammengesetzte Klasse für die Darstellung eines // Textes auf einer bestimmten Position in einer bestimmten Farbe // Der Text wird in einer string-Klasse abgelegt und die // Position in der GBase-Klasse // Hinweis: Eigentlich sollte die Klasse string als eingeschlossenene // Klasse definiert werden da hier eine has-a Beziehung besteht. class ColorString: public string, public GBase { unsigned long color; // zusätzliche Farb-Info public: // Konstruktor ColorString(int x, int y, const char* const pT, unsigned long col): string(pT), GBase(x, y), color(col) {} // Verschiebt Text void MoveIt(int x, int y) { SetPos(x, y); // GBase-Memberfunktion hierfür aufrufen } // Ändert den Text void ChangeText(const char* const pT) { assign(pT); // Memberfunktion assign() der string-Klasse } // Ausgabeoperator überladen friend std::ostream& operator << (std::ostream& os, const ColorString& colString); }; // Überladene Ausgabeoperator um ein ColorString auszugeben std::ostream& operator << (std::ostream& os, const ColorString& colString) { // colString.c_str() holt const char* auf den Text im string os << "Text : " << colString.c_str() << '\n'; // Position holen von GBase int x, y; colString.GetPos(x, y); os << "Position: (" << x << ',' << y << ")\n"; // Farb-Info ist eigene Eigenschaft os << "Farbe : 0x" << std::hex << colString.color << std::dec << endl; return os; } // main() Funktion int main() { // ColorString Objekt definieren ColorString myString(10,20,"ColorString",0xcc00cc); // und ausgeben cout << myString << endl; // ColorString mit neuem Text und neuer // Position versehen myString.ChangeText("ColorString modifiziert"); myString.MoveIt(200,200); // und erneut ausgeben cout << myString << endl; } |
Eine Übung entfällt an dieser Stelle!