ist die virtuelle Vererbung von rein abstrakten Klassen (Schnittstellen) notwendig

Warum ist es, dass in den folgenden code der compiler beschwert sich, dass PureAbstractBase ist ein Mehrdeutiger base Klasse MultiplyInheritedClass? Ich merke, ich habe zwei Kopien der PureAbstractBase im MultiplyInheritedClass und dass FirstConreteClass und SecondConreteClass abgeleitet werden sollten praktisch, weil Sie in der mittleren Zeile die Raute (und das ist in der Tat das problem zu beheben mit dem code unten). Aber obwohl ich zwei Kopien der Schnittstelle, warum ist es, dass der code in MultiplyInheritedClass nicht einfach überschreiben beiden und eindeutig wählen die interface-Klasse definiert MultiplyInheritedClass?

#include <iostream>
using namespace std;

class PureAbstractBase {
  public:
    virtual void interface() = 0;
};

//I know that changing the following line to:
//class FirstConcreteClass : public virtual PureAbstractBase {
//fixes the problem with this hierarchy
class FirstConcreteClass : public PureAbstractBase {
  public:
    virtual void interface() { implementation(); }
  private:
    void implementation() { cout << "This is object FirstConcreteClass\n"; }
};

//I know that changing the following line to:
//class SecondConcreteClass : public virtual PureAbstractBase {
//fixes the problem with this hierarchy
class SecondConcreteClass : public PureAbstractBase {
  public:
    virtual void interface() { implementation(); }
  private:
    void implementation() { cout << "This is object SecondConcreteClass\n"; }
};

class MultiplyInheritedClass : public FirstConcreteClass,
                               public SecondConcreteClass {
  public:
    virtual void interface() { implementation(); }
  private:
    void implementation() { cout << "This is object MultiplyInheritedClass\n"; }
};

Weiter, warum ich keine Probleme mit der folgenden Hierarchie? Nicht die ConcreteHandler Klasse haben drei Kopien des AbstractTaggingInterface in diesem Fall? Warum also nicht das gleiche Problem wie im obigen Beispiel?

#include <iostream>
using namespace std;

class AbstractTaggingInterface {
  public:
    virtual void taggingInterface() = 0;
};

class FirstAbstractHandler : public AbstractTaggingInterface {
  public:
    virtual void taggingInterface() { cout << "FirstAbstractHandler\n"; }
    virtual void handleFirst() = 0;
};

class SecondAbstractHandler : public AbstractTaggingInterface {
  public:
    virtual void taggingInterface() { cout << "SecondAbstractHandler\n"; }
    virtual void handleSecond() = 0;
};

class ThirdAbstractHandler : public AbstractTaggingInterface {
  public:
    virtual void taggingInterface() { cout << "ThridAbstractHandler\n"; }
    virtual void handleThird() = 0;
};

class ConcreteHandler : public FirstAbstractHandler,
                        public SecondAbstractHandler,
                        public ThirdAbstractHandler {
  public:
    virtual void taggingInterface() = { cout << "ConcreteHandler\n"; }
    virtual void handleFirst() {}
    virtual void handleSecond() {}
    virtual void handleThird() {}
};

Ich versuche, wickeln Sie meinen Kopf herum, dies alles, weil ich hatte ein Gespräch mit einem Kollegen vor kurzem, wo er behauptete, dass, wenn Sie waren Erben von rein virtuellen Klassen (Schnittstellen) ohne Daten-member dann virtuelle Vererbung nicht notwendig war. Ich glaube zu verstehen, warum die alten code-Beispiel funktioniert nicht und die letzteren würden gehen ein langer Weg zu bekommen gerade in meinem Kopf (und klar, was er genau meint mit seinem Kommentar). Vielen Dank im Voraus.

  • Ich weiß nicht, ob ich Ihre Frage verstehe, so könnten Sie das bitte klären? Soweit ich sehen kann, das zweite Beispiel ist nur Moor-standard-Vererbung ohne überraschungen. Das erste Beispiel ist unzureichend definiert, weil Sie multiplizieren Erben mehrdeutig Außerkraftsetzungen, die Sie lösen können mit der virtuellen Vererbung.
  • SB Danke für die Hilfe und sorry für die übereilte downvote (Sie sind jetzt von Ihnen positiv bewertet werden). Ich habe die zweite code-snippet zu gehören überschreibt. Ich bin sicher, dass meine Klarheit-Problem zu tun hat, mit meiner Verwirrung über das Problem. Ich denke, meine Frage ist, wie ist ConcreteHandler in der Lage zu Holen die richtige überschreibung für die taggingInterface Anruf in der Erwägung, dass MultiplyInheritedClass nicht in der Lage ist?
  • Keine sorgen 🙂 Also, was denken Sie, ist die Zweideutigkeit in dem zweiten Beispiel? An welchem Punkt sollte es nicht 100% klar, das überschreiben zu nehmen? Da alle Basis-Funktionen, rein virtuelle, Sie können nicht aufgerufen werden, so dass nur eine einzige Implementierung bleibt. Im ersten Beispiel gibt es mehrere gleich-gültig-Implementierungen. Denken Sie nur an wie ein PureAbstractBase* soll die eigentliche Funktion, die Sie wollen, ohne die notwendigen vtables (die Installation über virtuelle Vererbung).
  • SB Oh ... so der Grund, warum das zweite Beispiel funktioniert, weil ich kann nicht wirklich instanziieren, die eine der Grundlagen Klassen in diesem Fall? Ich kann mir vorstellen, dass bedeutet, dass, wenn ich auch eine der Handler-Funktionen nicht pure-virtual-dies wird auch brechen, genau wie im ersten Beispiel? Ich werde versuchen, und wenn dem so ist, aktualisieren Sie bitte Ihre Antwort zu reflektieren, Ihren Kommentar, und ich werde markieren Sie es als richtig.
  • Probieren Sie es einfach aus. Nehmen Sie mein einfaches Beispiel-code unten, zum Beispiel, und entfernen Sie den "virtual" - keywords und sehen, was der compiler sagt. Es wird alles einkochen, die Möglichkeit zur eindeutigen Bestimmung der richtigen angerufene von einem base-pointer, d.h. A * p = new C; p->foo();. Das Letzte Gespräch muss eindeutig bestimmbar sind, oder sonst erhalten Sie eine Fehlermeldung. (Beachten Sie, dass die Destruktoren müssen aufrufbar sein.)
  • SB Nur versucht haben, entfernen die abstrakte Methode aus einer der handler-Klassen, so dass es nicht rein-virtuelle und es noch kompiliert und druckt, was ich erwarte (ich habe eine triviale main, wo ich instaniate eine ConcreteHandler dynamisch und rufen Sie die tagging-inteface über einen Zeiger). Also ich denke, es gibt noch etwas ich bin nicht ganz immer.
  • Nach dem code, und was Sie erwarten passieren sollte und was tatsächlich passiert. (Halten Sie es minimal und kurz, denke ich.)
  • Sind Sie sicher, dass das erste Beispiel funktioniert nicht bei dir? Ich habe gerade versucht es auf Visual Studio 2008 und es hat sehr gut geklappt, nicht einmal eine Warnung! welchen compiler benutzt du?
  • SB ich denke, dass ich Durcheinander das Beispiel-code, so sieht es aus, wenn ich versuche, Sie zu instanziieren, die meisten abgeleitet, die über einen Zeiger auf die tagging-interface-Klasse beschwert es sich über Mehrdeutigkeit. Hier ist also eine Frage, sollte der zweite code-snippet "arbeiten" oder ist es schlechter code, die werfen compilation errors/warnings je nachdem, wie es verwendet?
  • gcc 4.5.1
  • Im zweiten Ausschnitt ist ziemlich Durcheinander im moment. Sie kann nicht schreiben, " = { }", und Sie falsch "Dritte"/"Dritte". Aber dann bist du immer noch fehlende virtuelle Vererbung! Fügen Sie code zum aufrufen des überschreiben, oder Sie könnten verpassen, die Fehler.
  • ja wie Sie schon in Ihrer Antwort das problem kommt erst, wenn die Anrufe an die Klassen sind aufgerufen, in gewisser Weise.
  • SB habe ich Fix die cut und paste-Fehler, wie Sie vorgeschlagen. Ich denke, dass das Problem genau das, was Sie und andere darauf hin: dass der code nicht kompiliert, wenn es aufgerufen wird, in gewisser Weise. Ich habe versucht, herauszufinden, der Kern etwas komplizierter und ich denke ich bekomme es jetzt: der code nie aufgerufen wird, wird über einen Zeiger auf die Basis so der Anruf ist nie eindeutig und folglich alles kompiliert in Ordnung, obwohl ich jetzt glaube, dass der code falsch ist. Nochmals vielen Dank.
  • SB, stellt sich gleichzeitig die Frage, dass, wenn es nie eine Notwendigkeit zum aufrufen des code auf diese Weise in die "original" - design, war ich versucht, zu umschreiben, dann ist die Vererbung der Rechte design überhaupt?
  • Der compiler ist sicherlich zu optimieren, Weg-features, die nicht verwendet werden, so können Sie manchmal Weg schreiben mehrdeutigen Definitionen, wenn diese nie benötigt werden... so ist es am besten, immer zu schreiben, eine vollständige test-first! (Und nach dem test SO zu zeigen, wie weit Sie schon habe 🙂 .) Als ob die Konstruktion sinnvoll ist, kannst nur du wissen. Wenn Ihre Klassen haben eine gemeinsame Schnittstelle, aber unterschiedliche spezifische Implementierungen, dann Vererbung richtig klingt, aber wenn Sie nie brauchen eine bestimmte Schnittstelle, vielleicht ist etwas falsch mit dem design dort...

InformationsquelleAutor bpw1621 | 2011-06-19
Schreibe einen Kommentar