C++ Tutorial

Abgeleitete Klassen

Eine abgeleitete Klasse wird eingesetzt, wenn:

1.) der Anwender eine vorgegebene Klasse um Eigenschaften und Methoden erweitern möchte, ohne den Quellcode der Ausgangsklasse zu modifizieren.

2.) Eigenschaften und Methoden, die in mehreren Klassen benötigt werden, in eine eigene Klasse ausgelagert werden soll.

Beispiel zu Punkt 1:

Sie haben eine Grafikbibliothek erworben, die eine Klasse zur Darstellung eines einfarbig ausgefüllten Rechtecks enthält. In der Anwendung benötigen Sie aber ein Rechteck, das mit einem Muster auszufüllen ist. Dazu kann die vorgegebene Klasse für das einfarbig ausgefüllte Rechteck als Basis verwendet werden und um die Eigenschaften des Musters erweitert werden. Und für diese Erweiterung wird nicht einmal den Quellcode der Ausgangsklasse benötigt!

Beispiel zu Punkt 2:

Eine Bibliothek zur Darstellung von grafischen Elementen enthält eine allgemeine Klasse mit den für jedes Grafikelement notwendigen Eigenschaften und Methoden, wie z.B. die Position und Farbe. Alle Grafikelemente verwenden dann diese allgemeine Klasse und fügen zu deren Member ihre spezifischen Member hinzu, wie z.B. den Radius für einen Kreis.

Weiter vorne haben Sie ebenfalls eine Möglichkeit kennengelernt, eine Klasse in ihrer Funktionalität zu erweitern, nämlich durch Einschließen von Objekten in eine Klasse. Und damit stellt sich die Frage, wann ein Objekt einer Klasse in eine andere Klasse eingeschlossen wird und wann eine Klasse abgeleitet wird. Als Faustregel können Sie sich folgenden Satz merken: Ein Objekt einer Klasse wird in eine andere Klasse eingeschlossen, wenn zwischen ihnen eine hat-eine (has-a) Beziehung besteht und abgeleitet wird, wenn eine ist-eine (is-a) Beziehung besteht.

Beispiel: Gegeben sei eine Klasse Color mit Farbinformationen und eine Klasse Win zur Darstellung eines Fensters. Da ein Fenster eine Farbe hat, wird die Klasse Color in Win eingeschlossen. Nun soll für eine abweichende Fensterdarstellung eine neue Fensterklasse SpecWin erstellt werden. In diesem Fall wird SpecWin von Win abgeleitet, da SpecWin ein spezielles Fenster ist.

Beispiele für Ableitungen

Sehen wir uns zunächst anhand eines Beispiels das Auslagern von gemeinsamen Member (Eigenschaften/Methoden) in eine eigene Klasse an.

Ausgangsbeispiel

Nachfolgend sind zwei Klassen für die Darstellung von Grafikelementen definiert. Die Klasse Frame soll zur Darstellung eines Rahmens dienen und die Klasse Bar zur Darstellung eines ausgefüllten Rechtecks. Beide Klassen besitzen Gemeinsamkeiten. So haben beide Klassen die Eigenschaft, dass sie eine Position und eine Größe besitzen. Außerdem stehen in beiden Klassen Methoden zur Verfügung, um diese Eigenschaften verändern zu können.

1: class Frame
2: {
3:    short xPos, yPos;     // identisch
4:    short width, height;  // identisch
5:    ...
6: public:
7:    Frame(...);
8:    void Draw() const;
9:    void SetPosition(...);   // identisch
10:   void SetSize(...);       // identisch
11: };
12: class Bar
13: {
14:    short xPos, yPos;     // identisch
15:    short width, height;  // identisch
16:    short fillColor;
17:    ...
18: public:
19:    Bar(...);
20:    void Draw() const;
21:    void SetPosition(...);    // identisch
22:    void SetSize(...);        // identisch
23: };

Die in beiden Klassen vorhandene Methode Draw() eignet sich nicht zum Auslagern, da die Objekte sicherlich unterschiedlich dargestellt werden.

Auslagern von Member

Laut vorheriger Aussage lassen sich die gemeinsamen Eigenschaften und Methoden in eine eigene Klasse auslagern. Im nachfolgenden Beispiel wurden die Gemeinsamkeiten in die Klasse GBase ausgelagert. Um diese ausgelagerten Eigenschaften und Methoden wieder zu den Klassen Frame und Bar 'hinzuzufügen', werden die beiden Klassen nachher von der Klasse GBase abgeleitet.

1: class GBase
2: {
3:    short xPos, yPos;
4:    short width, height;
5: public:
6:    GBase(...)
7:    void SetPosition(...);
8:    void SetSize(...);
9: };
10: class Frame // Ableitung von GBase einfügen
11: {
12:    ...
13: public:
14:    Frame(...);
15:    void Draw() const;
16: };
17: class Bar // Ableitung von GBase einfügen
18: {
19:    short fillColor;
20:    ...
21: public:
22:    Bar(...);
23:    void Draw() const;
24: };

Klasse erweitern

Sehen wir uns das Erweitern einer Klasse durch Ableitung an. Angenommen, wir haben die vorherigen Klassen für Grafiken in einer Klassenbibliothek erworben. Diese Bibliothek enthält u.a. die Klasse Bar für ein einfarbig ausgefülltes Rechteck. Wir benötigen aber ein Rechteck, das mit einem Muster ausgefüllt ist. In diesem Fall leiten wir eine neue Klasse PBar von der vorgegebenen Klasse Bar ab und fügen ihr die Eigenschaften für das Muster sowie die entsprechenden Methoden hinzu.

1: class PBar // Ableitung von Bar einfügen
2: {
3:    ...
4:    short pattern;
5: public:
6:    PBar(...);
7:    void Draw() const;
8:    void SetPattern(...);
9: };

Im Zusammenhang mit Ableitungen sind zwei neue Begriffe zu klären:

  • Basisklasse oder Superklasse
  • Abgeleitete Klasse oder Subklasse

Die Basisklasse ist diejenige Klasse, die ihre Eigenschaften und Methoden an die abgeleitete Klasse weitergibt (vererbt). Die abgeleitete Klasse erbt alle Eigenschaften und Methoden der Basisklasse, d.h., die abgeleitete Klasse verhält sich so als wären die geerbten Eigenschaften und Methoden innerhalb der abgeleiteten Klasse definiert.

Eine Klasse kann sowohl Basisklasse als auch abgeleitete Klasse gleichzeitig sein.

Zugriffsrecht protected

Bevor wir näher auf das Ableiten eingehen, sehen wir uns noch das fehlende Zugriffsrecht protected an. Das Zugriffsrecht protected ist eine Mischung aus den Zugriffsrechten public und private und kann überall dort stehen, wo Zugriffsrechte erlaubt sind. Auf die protected-Member einer Klasse kann nur von abgeleiteten Klassen (und der Klasse selbst) aus zugegriffen werden, d.h. sie verhalten aus der Sicht der abgeleiteten Klasse wie public-Member. Der Zugriff auf protected-Member über Objekte ist nicht möglich. Hier verhalten sich die protected-Member wie private-Member.

Ableitung

Das Ableiten einer Subklasse von einer Basisklasse erfolgt mit folgender Syntax:

class CSub: [ACCESS] CSuper
{...};

CSub ist der Name der abgeleiteten Klasse und CSuper der Name der Basisklasse. ACCESS gibt an, mit welchem Zugriffsrecht aus Sicht eines Objekts der abgeleiteten Klasse oder weiteren von CSub abgeleiteten Klassen die Member der Basisklasse in die abgeleitete Klasse übernommen werden. Für ACCESS können alle Zugriffsrechte (public, protected, private) eingesetzt werden. Wird ACCESS nicht spezifiziert, wird standardmäßig bei einer Ableitung von einer Klasse vom Typ class das Zugriffsrecht private angenommen und bei einer Ableitung von einer Klasse vom Typ struct das Zugriffsrecht public.

Eine abgeleitete Klasse hat nur Zugriff auf die public und protected-Member der Basisklasse, nicht aber auf deren private-Member. Im nachfolgenden Beispiel hat die Klasse Frame nur auf die Methoden SetPosition() und SetSize() der Basisklasse Zugriff, aber nicht auf deren Eigenschaften.

1: class GBase
2: {
3:    short xPos, yPos;
4:    short width, height;
5: public:
6:    GBase(...)
7:    void SetPosition(...);
8:    void SetSize(...);
9: };
10: class Frame: public GBase
11: {
12:    ...
13: public:
14:    Frame(...);
15:    void Draw() const;
16: };

Die abgeleitete Klasse erbt nicht nur die Eigenschaften und die 'normalen' Methoden ihrer Basisklasse, sondern auch deren überladene Operatoren, mit einer einzigen Ausnahme:

Alle anderen nicht-private überladenen Operatoren der Basisklasse stehen in der abgeleiteten Klasse ebenfalls zur Verfügung.

Wie gesagt, spielt das Zugriffsrecht ACCESS nur aus der Sicht eines Objekts oder einer weiteren abgeleiteten Klasse eine Rolle. Die abgeleitete Klasse hat immer Zugriff auf die public- und protected-Elemente ihrer Basisklasse.

Die public-Ableitung

Vorgegeben sind die drei Klassen GBase, Frame und MyFrame. GBase enthält die drei Member pub, prot und priv mit den entsprechenden Zugriffsrechten. Frame ist public von GBase abgeleitet und MyFrame public von Frame. Bei einer public-Ableitung werden die Zugriffsrechte aus der Basisklasse 1:1 in die abgeleitete Klasse übernommen. Damit kann über ein Objekt der abgeleiteten Klasse Frame sowie von Methoden von MyFrame auf die public-Member der Basisklasse zugegriffen werden.

1: // Definition der Basisklasse
2: class GBase
3: {
4: public:    short pub;
5: protected: short prot;
6: private:   short priv;
7: };
8: // Von GBase abgeleitete Klasse
9: class Frame: public GBase
10: {...};
11: // Weitere Klasse von Frame ableiten
12: class MyFrame: public Frame
13: {
14:    ...
15:    void AnyMethode()
16:    {
17:       pub = ...;    // ist erlaubt
18:       prot = ...;   // ist erlaubt
19:       priv = ...;   // ist nicht erlaubt
20:    }
21: }
22: // Objekt der Klassen Frame definieren
23: Frame frameObj;
24: // Zugriffe
25: frameObj.pub = ...;    // ist erlaubt
26: frameObj.prot = ...;   // ist nicht erlaubt
27: frameObj.priv = ...;   // ist nicht erlaubt

Die protected-Ableitung

Leiten wir die Klasse Frame protected von GBase ab. Die Ableitung der Klasse MyFrame lassen wir gegenüber vorhin unverändert auf public. Durch die protected-Ableitung werden alle public-Member der Basisklasse zu protected-Member der abgeleiteten Klasse. Der Unterschied zum vorherigen Beispiel liegt nur im Zugriffsrecht auf die public-Member der Basisklasse. Damit können zwar die Methoden der Klassen Frame und MyFrame immer noch auf das Member pub zugreifen, aber der Zugriff darauf über Objekte von Typ Frame und MyFrame ist nicht mehr möglich.

Selbstverständlich kann über ein Objekt der Basisklasse GBase immer auf das public-Member pub zugegriffen werden.

1: // Definition der Basisklasse
2: class GBase
3: {
4: public:    short pub;
5: protected: short prot;
6: private:   short priv;
7: };
8: // Von GBase abgeleitete Klasse
9: class Frame: protected GBase
10: {...};
11: // Weitere Klasse von Frame ableiten
12: class MyFrame: public Frame
13: {
14:    ...
15:    void AnyMethode()
16:    {
17:       pub = ...;    // ist erlaubt
18:       prot = ...;   // ist erlaubt
19:       priv = ...;   // ist nicht erlaubt
20:    }
21: }
22: // Objekt der Klassen Frame definieren
23: Frame frameObj;
24: // Zugriffe
25: frameObj.pub = ...;    // ist nicht erlaubt
26: frameObj.prot = ...;   // ist nicht erlaubt
27: frameObj.priv = ...;   // ist nicht erlaubt

Die private-Ableitung

Der letzte Fall, die private-Ableitung. Jetzt werden alle vererbten Member der Basisklasse zur private-Member der abgeleiteten Klasse. Damit besitzt nur noch die Klasse Frame Zugriff auf die public- und protected-Member der Basisklasse GBase. Selbst die Methoden der von Frame abgeleiteten Klasse MyFrame haben keinen Zugriff mehr auf die Member der Klasse GBase. Dies ist die restriktivste Form der Ableitung. Und auch Objekte vom Typ Frame haben hier ebenfalls keinen Zugriff mehr auf die Member der Basisklasse.

1: // Definition der Basisklasse
2: class GBase
3: {
4: public:    short pub;
5: protected: short prot;
6: private:   short priv;
7: };
8: // Von GBase abgeleitete Klasse
9: class Frame: private GBase
10: {...};
11: // Weitere Klasse von Frame ableiten
12: class MyFrame: public Frame
13: {
14:    ...
15:    void AnyMethode()
16:    {
17:       pub = ...;    // ist nicht erlaubt
18:       prot = ...;   // ist nicht erlaubt
19:       priv = ...;   // ist nicht erlaubt
20:    }
21: }
22: // Objekt der Klassen Frame definieren
23: Frame frameObj;
24: // Zugriffe
25: frameObj.pub = ...;    // ist nicht erlaubt
26: frameObj.prot = ...;   // ist nicht erlaubt
27: frameObj.priv = ...;   // ist nicht erlaubt

Zusammenfassung

Die nachfolgende Tabelle zeigt nochmals in einer Übersicht, wie sich das Zugriffsrecht beim Ableiten auf die Basisklassen-Member auswirkt:

ACCESS
Member Superklasse
Wird zu ... der Subklasse
private
private
kein Zugriff
protected
private-Member
public
private-Member
protected
private
kein Zugriff
protected
protected-Member
public
protected-Member
public
private
kein Zugriff
protected
protected-Member
public
public-Member

Konstruktor und Destruktor

Ausgangspunkt ist wieder die Klasse GBase sowie die von ihr abgeleitete Klasse Frame. Beide Klassen wurden um einen Konstruktor erweitert.

1: class GBase
2: {
3:    short xPos, yPos;
4:    short width, height;
5:    ...
6: public:
7:    GBase (short x, short y, short w, short h);
8:    ...
9: };
10: class Frame: public GBase
11: {
12:    ...
13:    short lineColor;
14: public:
15:    Frame(short x, short y, short w, short h,
16:          short lCol);
17: };
18: // Definition eines Frame-Objekts
19: Frame myFrame(10,20,640,480,0xC0C0CO);

Der Konstruktor der Klasse Frame erhält die Position und Größe sowie die Linienfarbe des Rahmens übergeben. Da Frame aber nur die Eigenschaft lineColor enthält, müssen die anderen Daten an die Basisklasse GBase weitergegeben werden. D.h., der Konstruktor von Frame muss den Konstruktor von GBase aufrufen.

Konstruktor der abgeleiteten Klasse

Der Aufruf des Konstruktors der Basisklasse erfolgt bei der Definition des Konstruktors der abgeleiteten Klasse mit folgender Syntax:

CSub(P1, P2,...): CSuper(Pa, Pb,...)
{...}

CSub ist wieder der Name der abgeleiteten Klasse und CSuper der Name der Basisklasse. D.h. nach der Parameterklammer des Konstruktors der abgeleiteten Klasse folgt zunächst ein Doppelpunkt und danach in der Initialisiererliste der Aufruf des Konstruktors der Basisklasse.

Damit kann der Konstruktor der Klasse Frame wie unten angegeben definiert werden. Wie zu sehen ist, werden die Parameter x, y, w und h einfach an den Konstruktor der Basisklasse GBase weitergegeben. Der Konstruktor von Frame speichert nur die Linienfarbe ab.

1: class GBase
2: {
3:    ...
4: public:
5:    GBase (short x, short y, short w, short h);
6:    ...
7: };
8: class Frame: public GBase
9: {
10:    ...
11:    short lineColor;
12: public:
13:    // Deklaration des ctor
14:    Frame(short x, short y, short w, short h,
15:          short lCol);
16: };
17: // Definition des ctor
18: Frame::Frame (short x, short y, short w,
19:               short h, short lCol):
20:        GBase(x, y, w, h), lineColor(lCol)
21: { }

Beachten Sie, dass der Aufruf des Konstruktors der Basisklasse nur bei der Definition des Konstruktors der abgeleiteten Klasse steht. An der Deklaration des Konstruktors vom Frame ändert sich nichts.

Konstruktor bei mehrstufigen Ableitungen

Doch wie sehen bei einer mehrstufigen Ableitung (GBase -> Frame -> MyFrame) die Definitionen der Konstruktoren aus? Bei einer mehrstufigen Ableitung ruft der Konstruktor einer abgeleiteten Klasse nur den Konstruktor seiner Basisklasse auf. D.h, der Konstruktor von MyFrame ruft nur den Konstruktor von Frame auf. Und der Konstruktor von Frame ruft dann wiederum den seiner Basisklasse GBase auf. Im Beispiel sehen Sie die prinzipiellen Definitionen aller drei Konstruktoren. Beim Aufruf des Konstruktors der Klasse Frame wird im Beispiel für den letzten Parameter zur Abwechselung eine Konstante übergeben.

1: // Konstruktor der Klasse GBase
2: GBase::GBase(short x, short y, short w, short h)
3: {...};
4: // Konstruktor der Klasse Frame
5: Frame::Frame(short x, short y, short w,
6:              short h, short lCol):
7:        GBase(x, y, w, h)
8: {...}
9: // Konstruktor der Klasse MyFrame
10: MyFrame::MyFrame(short x, short y, short w,
11:                  short h, short any):
12:          Frame(x, y, w, h, 0xFF)
13: {...}

Dieser Aufrufmechanismus funktioniert auch dann, wenn eine Basisklasse keinen Konstruktor oder nur den Standardkonstruktor besitzt. In diesem Fall braucht der Konstruktor der abgeleiteten Klasse nichts tun. Es wird immer automatisch der 'nächst höhere' Konstruktor aufgerufen.

Aufruf-Reihenfolge der Konstruktoren

Wird ein Objekt einer abgeleiteten Klasse definiert, wird zuerst der Konstruktor der Basisklasse ausgeführt, damit sichergestellt ist, dass beim Eintritt in den Konstruktor der abgeleiteten Klasse alle Eigenschaften der Basisklasse initialisiert sind. Im Beispiel werden die Klassen GBase, Frame und MyFrame mit den bekannten Ableitungen definiert. Da GBase die 'oberste' Basisklasse ist, wird deren Konstruktor als Erstes ausgeführt, anschließend der Konstruktor von Frame und zum Schluss der Konstruktor der 'untersten' Klasse MyFrame.

1: class GBase
2: {...};
3: class Frame: public GBase
4: {...};
5: class MyFrame: public Frame
6: {...};

Aufruf-Reihenfolge der Konstruktoren:

- Konstruktor von GBase
- Konstruktor von Frame
- Konstruktor von MyFrame

Aufruf-Reihenfolge der Destruktoren

Und was für die Konstruktoren gilt, gilt auch für die Destruktoren. Nur ist hier die ganze Sache einfacher, da ein Destruktor (fast) niemals direkt aufgerufen wird. Bei der Definition eines Destruktors muss also keine Rücksicht darauf genommen werden, ob die Klasse von einer anderen Klasse abgeleitet ist oder nicht. Wird ein Objekt, das in der Ableitungshierarchie ganz unten steht, entfernt, wird zuerst dessen Destruktor ausgeführt, anschließend der Destruktor seiner Basisklasse und dann der Destruktor der nächsthöheren Basisklasse. Die Aufruf-Reihenfolge ist hier genau umgekehrt wie bei den Konstruktoren. Damit würde sich folgende Aufruf-Reihenfolge der Destruktoren ergeben:

- Destruktor von MyFrame
- Destruktor von Frame
- Destruktor von GBase

Zugriff auf Basisklassen-Member

Wie erwähnt, können Methoden in abgeleitete Klassen auf die nicht-private-Member der Basisklasse zugreifen. Für den Zugriff reicht im Normalfall aus, wenn der Membername angegeben wird. Nicht-private-Member von Basisklassen verhalten sich für abgeleitete Klassen ja prinzipiell wie eigene Member. Im Beispiel wird aus der Methode DoAnything() der abgeleiteten Klasse auf die Eigenschaft xPos der Basisklasse zugegriffen und deren Methode SetSize() aufgerufen.

1: class GBase
2: {
3: protected:
4:    short xPos, yPos;
5:    short width, height;
6: public:
7:    GBase(...)
8:    void SetPosition(...);
9:    void SetSize(...);
10: };
11: class Frame: public GBase
12: {
13:    ...
14: public:
15:    Frame(...);
16:    void DoAnything();
17: };
18: // Zugriff auf Member der Basisklasse
19: void Frame::DoAnything()
20: {
21:    xPos -= 10;        // Dies sind Member der
22:    SetSize(100,100);  // der Basisklasse!
23:    ...
24: }

Inwieweit der direkte Zugriff auf Basisklassen-Eigenschaften einer sauberen Programmierung entspricht, sei dahingestellt. Das Beispiel dient nur zur Demonstration, wie auf Member einer Basisklasse zugegriffen werden kann.

Gleiche Member in Basis- und Sub-Klasse

Dieser direkte Zugriff funktioniert aber nur so lange, wie die abgeleitete Klasse kein Member mit gleichem Namen besitzt wie die Basisklasse. Besitzt die abgeleitete Klasse Member mit gleichem Namen wie die Basisklasse, wird standardmäßig auf das Member der eigenen Klasse zugegriffen. Um auf das Member der Basisklasse zuzugreifen, ist vor dem Membernamen der Name der Basisklasse anzugeben, gefolgt vom Zugriffsoperator ::.

1: class GBase
2: {
3: protected:
4:    short xPos, yPos;
5:    short width, height;
6: public:
7:    GBase(...)
8:    void SetPosition(...);
9:    void SetSize(...);
10: };
11: class Frame: public GBase
12: {
13:    ...
14:    short width;
15: public:
16:    Frame(...);
17:    void DoAnything();
18: };
19: // Zugriff auf Member der Basisklasse
20: void Frame::DoAnything()
21: {
22:    width++;          // Frame-Member
23:    GBase::width++;   // Basisklassen-Member
24: ...
25: }

Zugriff auf Basisklassen-Member über Objekte

Beim Zugriff über Objekte einer abgeleiteten Klasse auf die Basisklassen-Member reicht im Regelfall die Angabe des Membernamens aus. Der erste Zugriff unten über ein Objekt der Klasse Frame ruft die Methode SetPosition() der Klasse GBase auf. Ebenfalls wird die Methode SetSize() der Klasse GBase aufgerufen, wenn der Aufruf über ein weiteres abgeleitetes Objekt der Klasse MyFrame erfolgt. Dieser Zugriffsmechanismus funktioniert über mehrere Ebenen hinweg.

1: class GBase
2: {
3: protected:
4:    short xPos, yPos;
5:    short width, height;
6: public:
7:    GBase(...)
8:    void SetPosition(...);
9:    void SetSize(...);
10: };
11: class Frame: public GBase
12: {...};
13: class MyFrame: public Frame
14: {...};
15: // Objekt der abgeleiteten Klassen
16: Frame frameObj;
17: MyFrame myFrameObj;
18: // Zugriff auf public-Member von GBase
19: frameObj.SetPosition(...);
20: myFrameObj.SetSize(...);

Verdeckte Basisklassen-Member

Im Zusammenhang mit Aufrufen von Methoden einer Basisklasse soll auf eine kleine Stolperfalle hingewiesen werden. Sehen wir uns zunächst einmal die nachfolgend dargestellten Klassen an. Sowohl Frame als auch MyFrame besitzen jeweils eine Methode Move(). Die Methode der Klasse Frame besitzt jedoch einen Parameter und die der Klasse MyFrame zwei Parameter.

1: class Frame: public GBase
2: {
3: public:
4:    void Move(short x);
5:    ...
6: };
7: class MyFrame: public Frame
8: {
9: public:
10:    void Move(short x, short y);
11:    ...
12: };

Aufbauend auf diesen Klassendefinitionen sehen wir uns einige Aufrufe der Methode Move() an.

1: // Objekte der abgeleiteten Klassen
2: Frame frameObj;
3: MyFrame myFrameObj;
4: // Aufrufe der Methode Move(...)
5: frameObj.Move(10);           // Move() der Klasse Frame
6: myFrameObj.Move(10,20);      // Move() der Klasse MyFrame
7: myFrameObj.Move(10);         // Fehler!
8: myFrameObj.Frame::Move(10);  // Move() der Klasse Frame!

Bei den ersten beiden Aufrufen wird die zum Objekt gehörige Methode aufgerufen.

Einen Fehler verursacht dagegen der dritte Aufruf (Zeile 7). Was hier versucht wird, ist, die Methode Move() der Klasse Frame über ein MyFrame-Objekt aufzurufen, was zu einem Fehler führt. Member einer abgeleiteten Klasse verdeckt standardmäßig gleichnamige Member ihrer Basisklasse(n). Um trotzdem auf die Methode Move() der Klasse Frame über ein MyFrame-Objekt zuzugreifen, ist, wie im letzten Aufruf angegeben, nach dem Punktoperator der Klassenname, dann der Zugriffsoperator :: und danach die Methode anzugeben.

Basisklassenzeiger

Im Verbindung mit abgeleiteten Klassen sei an dieser Stelle vorab auf folgenden wichtigen Sachverhalt hingewiesen: Ein Zeiger vom Typ der Basisklasse kann Zeiger vom Typ einer abgeleiteten Klasse aufnehmen. Diese Eigenschaft spielt später bei den virtuellen Methoden eine entscheidende Rolle.

1: class GBase
2: {...};
3: class Frame: public GBase
4: {...};
5: class MyFrame: public Frame
6: {...}
7: int main()
8: {
9:    GBase *pBase; // Basisklassenzeiger
10:   // Frame-Objekt erstellen und Verweis darauf ablegen
11:   pBase = new Frame(...)
12:   ... // Frame-Objekt auch wieder löschen!!
13:   // MyFrame-Objekt erstellen, Verweis darauf ablegen
14:   pBase = new MyFrame(...);
15:   ... // MyFrame-Objekt auch wieder löschen!!
16: }

Einblenden von Basisklassen-Methoden

Methoden einer abgeleiteten Klasse verdecken bekanntlich gleichnamige Methoden ihrer Basisklasse. Nun kann es aber durchaus sinnvoll sein, dass gleichnamige Methoden der Basisklasse auch für Objekte der abgeleiteten Klasse verwendet werden. Hierzu ist in der abgeleiteten Klasse die Methode der Basisklasse mittels einer using-Anweisung einzublenden. Die allgemeine Syntax dieser using-Anweisung ist wie folgt:

using BASISKLASSE::MEMBER;

Sehen wir uns dazu das nachfolgende Beispiel an.

1: // Basisklasse
2: class Frame
3: {
4: public:
5:    // Move() mit einem Parameter
6:    void Move(short x);
7:    ...
8: };
9: // Abgeleitete Klasse
10: class MyFrame: public Frame
11: {
12: public:
13:    // Blendet Move() der Basisklasse ein
14:    using Frame::Move;
15:    // Move() mit zwei Parameter
16:    void Move(short x, short y);
17:    ...
18: };
19: int main()
20: {
21:    Frame frObj;      // Objekt der Basisklasse
22:    MyFrame mfrObj;   // Objekt der abgeleiteten Klasse
23:    // Move(short) der Basisklasse
24:    frObj.Move(10);
25:    // Move(short,short) der abgel. Klasse
26:    mfrObj.Move(10,30);
27:    // Move(short) der Basisklasse
28:    mfrObj.Move(20);
29: }

Sowohl die Basisklasse Frame als auch die davon abgeleitete Klasse MyFrame besitzen wieder eine Methode Move(). Die Methode der Basisklasse besitzt einen Parameter und die der abgeleiteten Klasse zwei Parameter. Um die Move() Methode der Basisklasse über ein Objekt der abgeleiteten Klasse aufrufen zu können, wird innerhalb der abgeleiteten Klasse mithilfe der using-Anweisung die Methode eingeblendet.

Hierbei ist zu beachten, dass zum einen die using-Anweisung innerhalb der public-Sektion der abgeleiteten Klasse steht. Und zum anderen wird nur der Name der Methode angegeben, also ohne deren Parameter. Daraus folgt: Enthält eine Basisklasse mehrere Methoden mit gleichem Namen, aber mit unterschiedlichen Parametern, werden alle Methoden mit diesem Namen eingeblendet.


Copyright 2024 © Wolfgang Schröder
E-Mail mit Fragen oder Kommentaren zu dieser Website an: info@cpp-tutor.de
Impressum & Datenschutz