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...
Du musst angemeldet sein, um einen Kommentar abzugeben.
Müssen Sie die virtuelle Vererbung zu überwinden, die Diamant-Mehrdeutigkeit:
Lange-winded Erklärung: Angenommen, Sie haben diese:
Die Vererbung von der virtuellen Funktion
foo
ist zweideutig, denn es kommt in drei Arten: ausB1
vonB2
und vonA
. Die erbschaft-Diagramm bildet ein "Diamant":Durch die Vererbung virtuelle
struct B1 : public virtual A;
etc., ermöglichen Sie jedem baseclass vonC*
zu nennen, die richtige Mitglied:Wir muss auch definieren
C::foo()
für diesen Sinn zu machen, da sonstC
würde nicht über ein klar definiertes Mitgliedfoo
.Einige weitere details: nehmen wir an, wir haben jetzt eine richtig praktisch-Erben-Klasse
C
wie oben. Wir können den Zugriff auf alle der verschiedenen virtuellen Mitglieder wie gewünscht:Update: Wie David sagt im Kommentar, der wichtige Punkt hier ist, dass die intermediate-Klassen
B1
undB2
Erben praktisch, so dass weitere Klassen (in diesem FallC
) können Erben vom Sie, während gleichzeitig halten Sie die Vererbung ausA
eindeutig. Sorry für die anfänglichen Fehler und danke für die Korrektur!MultiplyInheritedClass
ist der falsche Ort um das virtuelle Vererbung. Die virtuelle Vererbung eingesetzt werden, sowohl in derFirstConcreteClass
undSecondConcreteClass
. DieMultiplyInheritedClass
verwenden sollten einfache öffentlicher Vererbung (es sei denn, es könnte auch verwendet werden, in einigen anderen meta-Diamant-Muster).A * px = static_cast<B1*>(new C);
, und dannpx->foo()
würde Folgen Sie den entsprechenden Pfad (nämlichA
zuB1
zuC
).A
, so dass die gerade gegossen ist zweideutig - deshalb der Mittelstufe werfen.static_cast
wird verwendet, um eine operation durchführen, die sonst nicht (waren es nicht für den interm. Schritt) benötigt werden.error, ambiguous override
" warum?Deinem ersten Beispiel nicht, weil der compiler kann keine Verwechslungen zwischen den drei Implementierungen von
implementation()
. Sie überschreiben diese Methode inMultiplyInheritedClass
, die eigentlich überschreibt sowohlFirstConcreteClass::implementation
undSecondConcreteClass::implementation
(einmal virtuell, immer virtuell). Aber beide virtuellen fordert, noch existiert in der SchnittstelleMultiplyInheritedClass
, wodurch der Aufruf mehrdeutig an der call-site.Den Grund, dass dein Beispiel funktioniert, ohne
virtual
Vererbung ist, dass es keine widersprüchlichen Umsetzung der gemeinsamen Basisklasse. Anders ausgedrückt:Weil dein Beispiel hat rein virtuelle Funktionen, es gibt keine multiple-Implementierungen, die der compiler braucht, um keine Verwechslungen. Daher nur eine Implementierung vorhanden ist, und der Aufruf ist eindeutig.
=
Zeichen, die nicht dort sein sollte.) 2. Was bedeutet der call-site Aussehen? Dein erstes Beispiel hat ein call-site, das zweite Beispiel nicht.Mixed
einfach verstecktOne::DoSomething
undTwo::DoSomething
so dass es keine Zweideutigkeit. Der Kommentar bedeutet "Wirklich spielt keine Rolle, was Sie hier" so lange, wie Sie nicht umsetzenDoSomething()
.abc.One::DoSomething()
(woabc
ist einer von denen, gemischte Objekte) -- das wäre nicht sein Fehler, aber es ist ein Diamant-Muster (die Sie gerade entfernt die Mehrdeutigkeit).implementation
" (das ist nicht virtuell in OP ' s Beispiel)? etwas scheint hier falsch..PureAbstractBase *pab = &mic;
versucht wird (also das ist die mehrdeutige Konvertierung), d.h. der OP könnte die Methoden aufrufen, aber konnte nicht korrekt konvertiert werdenIch habe versucht, sowohl die Frage der codes und Sie funktionierte wunderbar, wenn das instanzieren eines Objekts der multi-Klasse geerbt. Es hat nicht funktioniert, nur mit Polymorphismus, wie diese zum Beispiel:
Und der Grund ist klar: Sie weiß es nicht, auf die Kopie von der Abstrakten Basis-Klasse verknüpft werden sollten (sorry für die schlecht Ausdrücke, ich verstehe die Idee aber nicht, es auszudrücken). Und da inherting virtaully macht nur eine Kopie vorhanden ist, in der abgeleiteten Klasse, dann ist es gut.
Auch den code von Billy ONeal überhaupt nicht klar, was sollten wir statt dessen von den Kommentaren?
Wenn wir Platz:
funktioniert es einwandfrei, weil keine Virtualität.
Arbeite ich auf Visual Studio 2008.
DoSomething
), Wenn Sie ein problem haben mit meiner Antwort, Kommentar auf meine Antwort. Nicht nur posten, etwas anderes zu kritisieren mich und hoffe, dass ich es gelesen habe.Warum nicht tun Sie es wie diese (vorgeschlagen in Benjamin Supnik blog-Eintrag):
gibt:
Diese Weise:
MultiplyInheritedClass