Sichtbarkeit bei Vererbung



  • Hallo,

    ich habe eine Klasse Base:

    class Base {
    protected:
       virtual void postUpdate() { }
    public:
       void update() {  postUpdate(); }
    };
    

    und von der leite ich jetzt ab und überschreibe postUpdate. Allerdings ändere ich die Sichtbarkeit von postUpdate auf private:

    class Derived : Base {
    private:
       virtual void postUpdate() { }
    
    };
    

    Zu meinem Erstaunen funktioniert das. Ich dachte man kann in Kindklassen die Sichtbarkeit nur erweitern? Also z.b. von protected auf public. Aber wieso kann ich sie hier einschränken?


  • Administrator

    mendelerbse schrieb:

    Aber wieso kann ich sie hier einschränken?

    Weil es erlaubt ist. Oder was willst du hören?
    Man kann public , protected oder private vererben. public und private haben durchaus einen Sinn, bei protected ist es mir bisher noch nicht klar geworden.

    Eine private Veerbung kann man zum Beispiel für sowas verwenden:

    class AbstractObject
    {
    protected:
      virtual void specialise() const = 0;
    
    public:
      void doSomething() const;
    };
    
    // Jetzt möchte ich ein ganz spezielles AbstractObject verwende,
    // es wird allerdings nur einmal vorkommen und sich in der Klasse
    // Foo befinden. Statt einer neuen Klasse zu erstellen, kann
    // ich eine private Vererbung machen.
    class Foo
      : private AbstractObject
    {
    private:
      virtual void specialise() const
      {
        // was spezielles ;)
      }
    };
    

    Eine private Vererbung ist also grundsätzlich eine has-a Beziehung, allerdings kann man das Objekt noch spezialisieren, da man Zugriff auf den protected Bereich hat.

    Grüssli



  • Du hast meine Frage nicht verstanden. Hab allerdings auch einen Fehler in meinem Code: Es muss heißen class Derived : public Base - also ne stink normale public Vererbung. Es geht mir hier um die Änderung der Sichtbarkeit einer METHODE!


  • Administrator

    mendelerbse schrieb:

    Du hast meine Frage nicht verstanden. Hab allerdings auch einen Fehler in meinem Code: Es muss heißen class Derived : public Base - also ne stink normale public Vererbung. Es geht mir hier um die Änderung der Sichtbarkeit einer METHODE!

    Achso, das ist kein Problem. Die Signatur der Funktion ist unabhängig von der Sichtbarkeit und damit die Polymorphie zieht, muss nur die Signatur gleich sein. Man kann also eine ableitende Klasse nicht dazu zwingen, die virtuellen Funktionen in irgendeiner Sichtbarkeit zu halten. Jede Klasse bestimmt dies ganz alleine.

    Grüssli



  • Mh, so ganz versteh ich das noch nicht. Ich kann doch zb nicht einfach eine public virtual methode in Base in der Derived Klasse zu einer proteced oder gar private methode machen? Das würde doch dieses Liskow Substitute Principle Ding (oder wie das heißt) verletzen. Sprich das hier:

    class Base {
    public:
    virtual void foo() { }
    };
    class Derived : public Base {
    proteced:
    virtual void foo() { }
    };
    
    Base* b = new Derived();
    b->foo(); // Aufruf geht nicht mehr (?), obwohl Base sagt, dass foo public ist
    

  • Mod

    mendelerbse schrieb:

    Mh, so ganz versteh ich das noch nicht. Ich kann doch zb nicht einfach eine public virtual methode in Base in der Derived Klasse zu einer proteced oder gar private methode machen? Das würde doch dieses Liskow Substitute Principle Ding (oder wie das heißt) verletzen. Sprich das hier:

    class Base {
    public:
    virtual void foo() { }
    };
    class Derived : public Base {
    proteced:
    virtual void foo() { }
    };
    
    Base* b = new Derived();
    b->foo(); // Aufruf geht nicht mehr (?), obwohl Base sagt, dass foo public ist
    

    Der Aufruf funktioniert sehr wohl. Die Erreichbarkeit von Membern hängt nur vom statischen Typ des Objektes ab und wird folglich nur beim Compilieren überprüft.

    Derived d;
    d.foo(); // fehler
    Base& b = d;
    b.foo(); // ok
    

    LSP ist also nicht verletzt, was allerdings auch nicht der entscheidende Maßstab ist. Denn natürlich kann die Sprache dich nicht daran hindern, eine Funktion so zu überschreiben, dass ihr Verhalten nicht mehr zu dem der Basisklasse passt. Es drängt sich kein Grund auf, der die Veränderung der Erreichbarkeit von Membern in beliebiger Weise verbieten müsste.



  • camper schrieb:

    Es drängt sich kein Grund auf, der die Veränderung der Erreichbarkeit von Membern in beliebiger Weise verbieten müsste.

    Finde ich schon. Wenn ich public ableite, dann sagte ich, dass Derived ein Base IST. Ist doch dann bescheuert, wenn ich aber bestimmte Methoden von Base nicht mehr aufrufen kann. Dadurch ist Derived ja kein Base mehr. Leuchtet mir absolut nicht ein, wieso nicht nur eine Erweiterung der Sichtbarkeit (also von protected auf public etc) erlaubt ist, sondern auch eine Einschränkung.


  • Administrator

    mendelerbse schrieb:

    Leuchtet mir absolut nicht ein, wieso nicht nur eine Erweiterung der Sichtbarkeit (also von protected auf public etc) erlaubt ist, sondern auch eine Einschränkung.

    Ich denke dein Fehler ist, dass du denkst, dass es die gleiche Funktion einfach überschreibt, was aber nicht der Fall ist. Es wird eine neue Funktion angelegt, welche die alte Funktion überdeckt. Das virtual bindet sie einfach in der vtable noch zusammen. Aber grundsätzlich ist es eine eigenständige Funktion. Soviel zum technischen Aspekt.
    Man sollte hier aber natürlich ein wenig vorsichtig sein, da man schnell Verwirrung stiften kann. Aber C++ schreibt halt kein Programmierstil oder sowas vor, sondern erlaubt meistens sehr vieles, was man auch für absolute Verwirrung missbrauchen kann. Bei C++ liegt es halt in der Verantwortung des Programmierers.

    Grüssli

    PS: WOW! Ein camper! *Fotoapparat auspackt* 😃


Anmelden zum Antworten