C++-Programmierung/ Objektorientierte Programmierung/ Methoden (nicht) überschreiben


Im letzten Kapitel haben Sie die Vererbung kennengelernt. Auch ist Ihnen bereits bekannt, dass eine Funktion oder Methode überladen werden kann, indem man für einen Funktionsnamen mehrere unterschiedliche Deklarationen tätigt. Es stellt sich nun also die Frage, wie der Compiler reagiert, wenn wir in einer abgeleiteten Klasse eine Methode deklarieren, die in der Basisklasse unter dem gleichen Namen bereits existiert.

Dieser Vorgang wird als Überschreiben einer Methode bezeichnet. Man könnte nun natürlich annehmen, dass das Überschreiben nur für die entsprechende Signatur gilt, dies ist jedoch nicht der Fall. Eine Methode gleichen Namens, verdeckt alle Überladungen in der Basisklasse. Folgendes Beispiel soll das Verhalten klären:

#include <iostream>

struct Base{
    void f(){ std::cout << "void Base::f()" << std::endl; }
    void f(int){ std::cout << "void Base::f(int)" << std::endl; }
};

struct A: Base{};

struct B: Base{
    void f(double){ std::cout << "void B::f(double)" << std::endl; }
};

int main(){
    A a; // Nutzt f() aus Base, da keine eigene Methode f() existiert
    B b; // Überschreibt alle Methoden f()

    a.f();  // void Base::f();
    a.f(5); // void Base::f(int);

    // b.f(); // Compilierfehler: kein passendes f() in B; Base::f() ist verdeckt
    b.f(5.4); // void B::f(double);
    b.f(5);   // void B::f(double); (implizite Konvertierung nach double)

    // expliziter Aufruf der Basisklassenmethoden
    b.Base::f();    // void Base::f();
    b.Base::f(5.4); // void Base::f(int); (implizite Konvertierung nach int)
    b.Base::f(5);   // void Base::f(int);
}

Wie sie sehen, können die Methoden der Basisklasse durch explizite Angabe der selben aufgerufen werden. Alternativ wäre auch ein static_cast von b möglich, dies führt jedoch zu schlecht lesbarem und fehleranfälligen Code und sollte daher vermieden werden. Fehleranfällig ist er, weil ein static_cast natürlich eine willkürliche Konvertierung bewirken kann, also in einen Typen von dem b gar nicht abgeleitet ist, aber auch wenn das Konvertierungsziel eine Basisklasse ist, können sich unerwartete Effekte einstellen:

Schlechter Stil! Bitte nicht verwenden!


int main(){
    B b; // Überschreibt alle Methoden f()

    // expliziter Aufruf der Basisklassenmethoden
    static_cast< Base& >(b).Base::f(); // Gleichwertig zu "b.Base::f()"
    static_cast< Base >(b).Base::f(); // Erzeugt eine temporäre Kopie von a und ruft für diese Base::f() auf
}

Um zu sehen, dass tatsächlich eine Kopie erzeugt wird, können sie Base einen entsprechenden Kopierkonstruktor hinzufügen. Natürlich benötigt Base dann auch einen Standardkonstruktor.

Hinweis

Im Kapitel Virtuelle Methoden werden Sie eine scheinbar ähnliche Technik kennenlernen, diese hat jedoch im Gegensatz zum Überschreiben von Methoden nichts mit Funktionsüberladung zu tun und sollte keinesfalls damit verwechselt werden!