2012-04-20 10 views
5

Basisklasse A hat eine Unterklasse B, und B hat eine Unterklasse C. A implementiert eine virtuelle Methode doStuff(), B nicht, und C tut. Von C, ich nennen A wollen ‚Implementierung s von doStuff() (Ich tue dies aus C‘ s Umsetzung von doStuff() aber das ist nicht wirklich wichtig sollte.) Soll ich anrufen:Aufrufen von identisch benannten Methoden in Basisklassen

A::doStuff(); 

Oder:

B::doStuff(); 

Das erste scheint klarer zu sein, weil es sich auf die tatsächliche Implementierung bezieht. Auf der anderen Seite könnte der zweite nützlicher sein, wenn ich später entscheide, dass B zu doStuff() anders als A muss. Welches ist mehr Standard? Was ist gefährlicher?

Ich war ein wenig überrascht, dass B::doStuff() keine Warnungen ausgelöst haben, aber ich nehme an, dass per Definition B eine Implementierung hat, auch wenn es von der Basisklasse ist. Kann die Kette von Basisklassen und Implementierungen beliebig lang und kompliziert sein? Zum Beispiel, wenn ich A bis Z hätte, jede eine Unterklasse der vorherigen, könnte ich Implementierungen irgendwo in der Kette haben, und ruft die Methode einer Klasse einfach "die Kette hoch", bis sie eine Implementierung findet?

Antwort

2

Erstens, in Bezug auf den letzten Absatz, ja, Sie haben absolut Recht.

Weiter oben, für die Hauptfrage:

Es ist eine Frage der Logik, die auf einer Fall-zu-Fall-Basis behandelt werden soll. Nur Sie können entscheiden, ob Sie die Logik von B oder von A aufrufen möchten, auch wenn B::doStuff() noch nicht implementiert ist. Es gibt keine richtige Antwort.

Allerdings sollten Sie in die Vorlage Methode Muster schauen. Meine Vermutung ist, dass, wenn Sie C anrufen möchten, etwas von A und später Sie entscheiden, dass Sie wollen (Geschwister von C), das gleiche zu tun, du bist Code zu duplizieren. Also würde ich einen anderen Ansatz nehmen:

class A 
{ 
private: 
    void doAStuff(); //doesn't have to be here 
public: 
    virtual void doStuff() 
    { 
     doAStuff(); 
    } 
    void doAStuffAndStuff(); 
    { 
     doAStuff(); 
     doStuff(); 
    } 
} 

class B : A 
{ 
public: 
    virtual void doStuff(); //or not 
} 

class C : B 
{ 
public: 
    virtual void doStuff(); 
} 

IMO das ist intuitiver. Sagen wir, ich habe eine A* a, ich habe nicht den dynamischen Typ von a wissen:

  • wenn ich doStuff() mit der Logik in den meisten abgeleiteten Klasse aufrufen wollen, rufe ich doStuff().
  • wenn ich doStuff() mit der Logik in der abgeleiteten Klasse aufrufen möchte, aber ich möchte auch die Logik von A, rufe ich doAStuffAndStuff().

Ich sage diesen Ansatz als Alternative. Ich sage nicht, dass es für Ihren Fall notwendigerweise gilt, aber es könnte.

+0

Das sieht wie eine sehr saubere und nützliche Alternative aus. – Luke

0

Anruf B::doStuff. Noch besser, verwenden Sie keine Designs, die Methoden der Basisklassen explizit aufrufen müssen, wenn Sie können.Wenn Sie müssen, simulieren Sie base.doStuff durch ein Makro oder Alias-Konstrukt, um genau diese Art von Fehlern zu verhindern. Sie können einen Alias ​​definieren (und sicherstellen, dass er zur Kompilierungszeit vorhanden ist) BaseType in der Klasse verwenden und BaseType::doStuff Syntax verwenden.

war ich ein wenig überrascht, dass B :: doStuff() alle Warnungen nicht auslösen, aber ich nehme an, dass per Definition B eine Implementierung hat, auch wenn es von der Basisklasse ist

Es gibt nichts zu warnen. Es ist in Ihrem Fall ein legales Szenario.

Kann die Kette von Basisklassen und Implementierungen und komplizierte

Ja, es kann beliebig lang sein, aber es sollte nicht. Designs, die lange Vererbungsketten erfordern und einige versteckte Regeln enthalten, welche Methoden welche Basisklassen aufgerufen werden sollten, sind normalerweise kaputt.

Verwandte Themen