Frage

Ich möchte wissen, was für ein „virtuelle Basisklasse„ist und was es bedeutet.

Lassen Sie mich ein Beispiel zeigen:

class Foo
{
public:
    void DoSomething() { /* ... */ }
};

class Bar : public virtual Foo
{
public:
    void DoSpecific() { /* ... */ }
};
War es hilfreich?

Lösung

Virtuelle Basisklassen, die bei der virtuellen Vererbung verwendet werden, sind eine Möglichkeit, zu verhindern, dass bei Verwendung der Mehrfachvererbung mehrere „Instanzen“ einer bestimmten Klasse in einer Vererbungshierarchie erscheinen.

Stellen Sie sich das folgende Szenario vor:

class A { public: void Foo() {} };
class B : public A {};
class C : public A {};
class D : public B, public C {};

Die obige Klassenhierarchie führt zu dem „gefürchteten Diamanten“, der wie folgt aussieht:

  A
 / \
B   C
 \ /
  D

Eine Instanz von D besteht aus B, das A enthält, und C, das auch A enthält.Sie haben also zwei „Instanzen“ (in Ermangelung eines besseren Ausdrucks) von A.

Wenn Sie dieses Szenario haben, besteht die Möglichkeit von Unklarheiten.Was passiert, wenn Sie dies tun:

D d;
d.Foo(); // is this B's Foo() or C's Foo() ??

Die virtuelle Vererbung soll dieses Problem lösen.Wenn Sie beim Erben Ihrer Klassen „virtual“ angeben, teilen Sie dem Compiler mit, dass Sie nur eine einzige Instanz benötigen.

class A { public: void Foo() {} };
class B : public virtual A {};
class C : public virtual A {};
class D : public B, public C {};

Dies bedeutet, dass es nur eine „Instanz“ von A in der Hierarchie gibt.Somit

D d;
d.Foo(); // no longer ambiguous

Ich hoffe, das hilft als Kurzzusammenfassung.Weitere Informationen finden Sie unter Das Und Das.Ein gutes Beispiel ist ebenfalls verfügbar Hier.

Andere Tipps

Über das Speicherlayout

Nebenbei bemerkt besteht das Problem mit dem Dreaded Diamond darin, dass die Basisklasse mehrfach vorhanden ist.Sie glauben also, dass Sie bei einer regulären Vererbung Folgendes haben:

  A
 / \
B   C
 \ /
  D

Aber im Speicherlayout haben Sie:

A   A
|   |
B   C
 \ /
  D

Dies erklärt, warum, wenn Sie anrufen D::foo(), Sie haben ein Mehrdeutigkeitsproblem.Aber die real Das Problem tritt auf, wenn Sie eine Mitgliedsvariable von verwenden möchten A.Nehmen wir zum Beispiel an, wir haben:

class A
{
    public :
       foo() ;
       int m_iValue ;
} ;

Wenn Sie versuchen, darauf zuzugreifen m_iValue aus D, wird der Compiler protestieren, da er in der Hierarchie zwei sieht m_iValue, nicht eins.Und wenn Sie eines ändern, sagen wir: B::m_iValue (das ist das A::m_iValue Elternteil von B), C::m_iValue wird nicht geändert (das ist die A::m_iValue Elternteil von C).

Hier bietet sich die virtuelle Vererbung an, denn damit kehren Sie zu einem echten Diamanten-Layout zurück, mit nicht nur einem foo() nicht nur eine Methode, sondern auch eine und nur eine m_iValue.

Was könnte schiefgehen?

Vorstellen:

  • A hat einige grundlegende Funktionen.
  • B fügt eine Art cooles Array von Daten hinzu (zum Beispiel)
  • C fügt einige coole Funktionen wie ein Beobachtermuster hinzu (z. B. on m_iValue).
  • D erbt von B Und C, und somit von A.

Bei normaler Vererbung, modifizierend m_iValue aus D ist nicht eindeutig und muss gelöst werden.Selbst wenn es so ist, gibt es zwei m_iValues innen D, also sollten Sie sich das besser merken und beide gleichzeitig aktualisieren.

Mit virtueller Vererbung, Veränderung m_iValue aus D ist in Ordnung...Aber...Nehmen wir an, dass Sie es getan haben D.Durch seine C Schnittstelle haben Sie einen Beobachter angehängt.Und durch es B Schnittstelle aktualisieren Sie das coole Array, was den Nebeneffekt hat, dass es sich direkt ändert m_iValue...

Als die Änderung von m_iValue erfolgt direkt (ohne Verwendung einer virtuellen Zugriffsmethode), wobei der Beobachter „zuhört“. C wird nicht aufgerufen, da der Code, der das Abhören implementiert, in ist C, Und B weiß es nicht...

Abschluss

Wenn Sie eine Raute in Ihrer Hierarchie haben, bedeutet das, dass Sie zu 95 % in dieser Hierarchie etwas falsch gemacht haben.

Um die Mehrfachvererbung mit virtuellen Basen zu erklären, sind Kenntnisse des C++-Objektmodells erforderlich.Und das Thema klar zu erklären, gelingt am besten in einem Artikel und nicht in einem Kommentarfeld.

Die beste und lesbarste Erklärung, die ich gefunden habe und die alle meine Zweifel zu diesem Thema ausräumte, war dieser Artikel: http://www.phpcompiler.org/articles/virtualinheritance.html

Nachdem Sie das gelesen haben, müssen Sie wirklich nichts mehr zu diesem Thema lesen (es sei denn, Sie sind Compiler-Autor) ...

Eine virtuelle Basisklasse ist eine Klasse, die nicht instanziiert werden kann:Sie können kein direktes Objekt daraus erstellen.

Ich denke, Sie verwechseln zwei sehr unterschiedliche Dinge.Virtuelle Vererbung ist nicht dasselbe wie eine abstrakte Klasse.Virtuelle Vererbung verändert das Verhalten von Funktionsaufrufen;Manchmal löst es Funktionsaufrufe auf, die andernfalls mehrdeutig wären, manchmal verschiebt es die Verarbeitung von Funktionsaufrufen auf eine andere Klasse als die, die man bei einer nicht-virtuellen Vererbung erwarten würde.

Ich möchte die freundlichen Klarstellungen von OJ ergänzen.

Virtuelles Erbe ist nicht ohne Preis.Wie bei allen virtuellen Dingen erhalten Sie einen Leistungseinbruch.Es gibt einen Weg, diesen Leistungseinbruch zu umgehen, der möglicherweise weniger elegant ist.

Anstatt den Diamanten durch virtuelles Ableiten zu zerbrechen, können Sie dem Diamanten eine weitere Ebene hinzufügen, um etwa Folgendes zu erhalten:

   B
  / \
D11 D12
 |   |
D21 D22
 \   /
  DD

Keine der Klassen erbt virtuell, alle erben öffentlich.Die Klassen D21 und D22 verbergen dann die virtuelle Funktion f(), die für DD nicht eindeutig ist, möglicherweise indem sie die Funktion als privat deklarieren.Sie definierten jeweils eine Wrapper-Funktion, f1() bzw. f2(), und ruften jeweils die klassenlokale (private) Funktion f() auf, um so Konflikte zu lösen.Die Klasse DD ruft f1() auf, wenn sie D11::f() möchte, und f2(), wenn sie D12::f() möchte.Wenn Sie die Wrapper inline definieren, entsteht wahrscheinlich kein Overhead.

Wenn Sie D11 und D12 ändern können, können Sie natürlich den gleichen Trick auch innerhalb dieser Klassen anwenden, aber oft ist das nicht der Fall.

Zusätzlich zu dem, was bereits über Mehrfach- und virtuelle Erbschaften gesagt wurde, gibt es einen sehr interessanten Artikel in Dr. Dobb's Journal: Mehrfachvererbung wird als nützlich erachtet

Du bist etwas verwirrend.Ich weiß nicht, ob Sie einige Konzepte verwechseln.

Sie haben keine virtuelle Basisklasse in Ihrem OP.Sie haben nur eine Basisklasse.

Sie haben eine virtuelle Vererbung vorgenommen.Dies wird normalerweise bei der Mehrfachvererbung verwendet, sodass mehrere abgeleitete Klassen die Mitglieder der Basisklasse verwenden, ohne sie zu reproduzieren.

Eine Basisklasse mit einer rein virtuellen Funktion kann nicht instanziiert werden.Dies erfordert die Syntax, die Paul versteht.Es wird normalerweise verwendet, damit abgeleitete Klassen diese Funktionen definieren müssen.

Ich möchte das nicht näher erläutern, da ich nicht ganz verstehe, was Sie fragen.

Das bedeutet, dass ein Aufruf einer virtuellen Funktion an die „richtige“ Klasse weitergeleitet wird.

C++ FAQ Lite FTW.

Kurz gesagt, es wird häufig in Mehrfachvererbungsszenarien verwendet, in denen eine „Diamant“-Hierarchie gebildet wird.Durch die virtuelle Vererbung wird dann die Mehrdeutigkeit beseitigt, die in der untersten Klasse entsteht, wenn Sie eine Funktion in dieser Klasse aufrufen und die Funktion entweder in die Klasse D1 oder D2 über dieser untersten Klasse aufgelöst werden muss.Siehe die FAQ-Artikel für ein Diagramm und Details.

Es wird auch in verwendet Schwesterdelegation, eine leistungsstarke Funktion (wenn auch nichts für schwache Nerven).Sehen Das FAQ.

Siehe auch Punkt 40 in Effective C++ 3. Auflage (43 in 2. Auflage).

Beispiel für die lauffähige Verwendung von Diamond Inheritance

Dieses Beispiel zeigt, wie eine virtuelle Basisklasse in einem typischen Szenario verwendet wird:um die Diamantenvererbung zu lösen.

#include <cassert>

class A {
    public:
        A(){}
        A(int i) : i(i) {}
        int i;
        virtual int f() = 0;
        virtual int g() = 0;
        virtual int h() = 0;
};

class B : public virtual A {
    public:
        B(int j) : j(j) {}
        int j;
        virtual int f() { return this->i + this->j; }
};

class C : public virtual A {
    public:
        C(int k) : k(k) {}
        int k;
        virtual int g() { return this->i + this->k; }
};

class D : public B, public C {
    public:
        D(int i, int j, int k) : A(i), B(j), C(k) {}
        virtual int h() { return this->i + this->j + this->k; }
};

int main() {
    D d = D(1, 2, 4);
    assert(d.f() == 3);
    assert(d.g() == 5);
    assert(d.h() == 7);
}

Virtuelle Klassen sind nicht das Gleiche wie virtuelle Vererbung.Virtuelle Klassen können Sie nicht instanziieren, virtuelle Vererbung ist etwas ganz anderes.

Wikipedia beschreibt es besser als ich. http://en.wikipedia.org/wiki/Virtual_inheritance

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top