Logo BKs Home - Ein neuer Versuch. Logo

C++ FAQs

   
C++ makes it much harder to shoot yourself in the foot, but when you do, it blows off your whole leg.
- Bjarne Stroustrup

In C we had to code our own bugs. In C++ we can inherit them.
-Prof. Gerald Karam

    
   

Einleitung
Was hat es mit diesen C++ FAQs aufsich?  updated 

C++ allgemein
Sollte man Variablen zu Beginn eines Blocks deklarieren?save
Was muss man beim Test auf eof() beim Lesen aus C++ Streams beachten?save
Wie lassen sich Speicher- bzw. Ressourcenlöcher vermeiden?save
Was sind auto_ptr und wie setzt man sie ein?save
Was ist falsch an void main() { /* ... */ } bzw. main() { /* ... */ }?save
Wie macht man aus einer Zahl einen String?save
Sollte man <iostream.h> oder <iostream> verwenden?  updated 
Was ist eine using-Deklaration?save
Was ist eine using-Direktive?save
Warum kann man Methoden von Basisklassen nicht in abgeleiteten Klassen überladen?save
Was bedeutet das Schlüsselwort static?save
Was ist der Unterschied zwischen <name.h> und <cname>? 

C++ speziell
Wird für den folgenden Code ein Standardkonstruktor erzeugt? Werden die Member initialisiert?save
Was besagt die one definition rule (ODR)?  updatedsave
Was versteht man unter "Koenig Lookup"?save
Wann wird ein Standardkonstruktor erzeugt? Und was tut er?save
Was ist die Regel der großen Drei (Law of the big three)?save
Wie kann man Strings case-insensitiv vergleichen?save
Wird für T x = u; der Zuweisungsoperator von T aufgerufen?save
Ist Zuweisung nicht trivialer Objekte immer langsamer als Initialisierung?save
Überladen, Überschreiben, Überdecken - Was ist was? 

Anderes
Was ist richtige Vererbung und was hat dies mit dem Liskov-Prinzip zu tun?save
Was ist das open-closed Principle?save
Was ist schlecht an globalen Variablen?save
Wie kann man den Inhalt eines Verzeichnisses auflisten?  updated 
Was ist const-correctness?save
Was ist das Singleton-Pattern und wie wird es in C++ implementiert?save
Was bedeutet "named return value optimization"?save
Wie erhält man richtige Vererbung ohne Liskov-Prinzip?save
Warum sollten Instanzvariablen immer private sein?save



zurück zum Seitenanfang
   Q: Was ist richtige Vererbung und was hat dies mit dem Liskov-Prinzip zu tun?

Richtige Vererbung bedeutet, dass das Verhalten einer abgeleiteten Klasse und das Verhalten der Basisklasse austauschbar sind.

Eine genauere Definiton stammt von Barbara Liskov:

If for each object o1 of type S there is an object o2 of type T such that for all programs P defined in terms of T, the behavior of P is unchanged when o1 is substituted for o2 then S is a subtype of T.

Umgangssprachlich kann man das etwa so ausdrücken:

Erfüllt eine Funktion die einen Pointer oder eine Referenz auf eine Basisklasse erwartet eine bestimmte Aufgabe, so muss diese Funktion die selbe Aufgabe auch mit einem Objekt einer abgeleiteten Klasse erfüllen können.

Normalerweise sagt man, dass Vererbung eine "is-A"- Beziehung ausdrücken soll. Das diese Forderung häufig zu vage ist, zeigt das folgende Beispiel:

class Rechteck
{
    private:
        double Breite;
        double Hoehe;
    public:
        void SetBreite(double b) {Breite = b;}
        void SetHoehe(double h) {Hoehe = h;}
        double GetBreite() const {return Breite;}
        double GetHoehe() const {return Hoehe;}
};

Soweit so gut. Nun soll als nächstes eine Klasse Quadrat dem System hinzugefügt werden. Da Quadrat immer auch ein Rechteck ist ("is-A"), lassen wir Quadrat von Rechteck erben. Nun tritt aber schon das erste Problem ein. Quadrat erbt sowohl SetHoehe als auch SetBreite. Diese beiden Methoden machen so für ein Quadrat natürlich keinen Sinn, da Hoehe und Breite nicht unabhängig von einander verändert werden dürfen. Natürlich läßt sich das schnell korrigieren:

class Quadrat : public Rechteck
{
    public:
        void SetHoehe(double h)
        {
            Rechteck::SetBreite(h);
            Rechteck::SetHoehe(h);
        }

        void SetBreite(double b)
        {
            Rechteck::SetHoehe(b);
            Rechteck::SetBreite(b);
        }
};

Nun werden Breite und Höhe zusammen verändert und das Quadrat bleibt immer ein Quadrat. Leider ist das noch lange nicht das Ende vom Lied. In einem Anwendungsprogramm könnte folgende Funktion stehen:

void f(Rechteck& re)
{
    re.SetBreite(3.4);
}

Übergibt man dieser Funktion ein Objekt vom Typ Quadrat, hat dies zur Folge, dass das Quadrat danach kein Quadrat mehr ist. Da die Set-Methoden der Basisklasse nicht als virtuell deklariert sind, wird natürlich die Methode SetBreite von Rechteck aufgerufen. Diese weiß natürlich nichts von den Einschränkungen, die für ein Quadrat gelten und verändert die Breite des Quadratsobjekts ohne dabei auch die Höhe zu verändern. Diese Funktion ist also ein klares Beispiel für eine Verletzung des Liskov-Prinzips.

Nun könnte man natürlich nachträglich die Klasse Rechteck verändern und die Methoden SetBreite und SetHoehe virtuell machen. Eine solche Änderung ist im Nachhinein aber nur schwer möglich, da man damit bereits vorhandenen Code ungültig macht. Nehmen wir aber trotzdem einmal an, wir könnten Rechteck verändern und die beiden Set-Methoden wären nun virtuell. Die obere Funktion würde nun einwandfrei funktionieren.

Aber was ist hiermit?

void g(Rechteck& re)
{
    re.SetBreite(3);
    re.SetHoehe(4);
    double Area = re.GetBreite() * re.GetHoehe();
    assert(Area == 12);
}

Die Funktion trifft eine sehr logische Annahme. Trotzdem wird diese Funktion abbrechen, falls ihr ein Objekt vom Typ Quadrat übergeben wird. Trotz "is-A" Beziehung widerspricht dieses Beispiel also dem Liskov-Prinzip.

Fazit:
Im Sinne des Liskov-Prinzips ist ein Quadrat kein Rechteck, da das Verhalten eines Quadrats und das Verhalten eines Rechtecks nicht austauschbar sind. Das Liskov-Prinzip und damit auch richtige Vererbung basiert immer auf Verhalten. Eine abgeleitete Klasse muss sich immer so verhalten, wie es die Benutzer von der Basisklasse erwarten.

Literatur:
   
   
Ihr Feedback ist mir wichtig:
Wie würden Sie diese Antwort bewerten? Als:     
Haben Sie Fragen, Ergänzungen oder Verbesserungen? Schreiben Sie mir: hume@c-plusplus.de
zurück zum Seitenanfang
    Zuletzt aktualisiert am: 13.01.2007 zurück zur Startseite    
    © Benjamin Kaufmann - Fragen, Kritik und Anregungen bitte direkt an mich oder ins Gästebuch