Frage

Betrachten Sie die folgende Klassenhierarchie:

  • Basisklassenobjekt mit einer virtuellen Methode foo()
  • eine beliebige Hierarchie mit mehrfacher Vererbung (virtuell und nicht virtuell);Jede Klasse ist ein Subtyp von Object.Einige von ihnen überschreiben foo(), andere nicht
  • eine Klasse X aus dieser Hierarchie, die foo() nicht überschreibt

Wie kann festgestellt werden, welche Methode bei einem Aufruf von foo() für ein Objekt der Klasse X in C++ ausgeführt wird?

(Ich suche nach dem Algorithmus, nicht nach einem bestimmten Fall.)

War es hilfreich?

Lösung

Es gibt kein MRO in C ++ wie Python. Wenn ein Verfahren mehrdeutig ist, ist es ein Kompilierzeitfehler. Ob eine Methode virtuell oder nicht beeinflusst, beeinflusst es nicht, sondern virtuell erbschaft wird.


Der Algorithmus ist in dem C ++-Standard § [Klasse.Member.Lookup] (10.2) beschrieben. Grundsätzlich wird es die nächstgelegene eindeutige Implementierung in der Superklasse-Grafik finden. Der Algorithmus funktioniert so:

    .
  1. Angenommen, Sie möchten eine Funktion f in der Klasse c nachschlagen.

  2. Wir definieren einen -Andruck-up-Set s (f, c) ein Paar Sätze ( δ , Σ ), die alle Möglichkeiten darstellen. (§10.2 / 3)

      .
    • Der Satz δ wird als deklaration set bezeichnet, der im Grunde alle möglichen f ist.

    • Das Set σ wird als -unterobjektsatz bezeichnet, der die Klassen enthält, die diese f gefunden werden.

  3. lässt s (f, c) alle f direkt definiert (oder using-ed) in c , falls vorhanden (§10.2 / 4) :

    Δ = {f in C};
    if (Δ != empty)
      Σ = {C};
    else
      Σ = empty;
    S(f, C) = (Δ, Σ);
    
  4. wenn s (f, c) leer ist (§10.2 / 5) ,

      .
    • berechnen s (f, b i ) wobei b i eine Basisklasse von ist c , für alle i .

    • verschmelzen jedes s (f, b i ) in s (f, c) nacheinander. .

      if (S(f, C) == (empty, empty)) {
        B = base classes of C;
        for (Bi in B)
          S(f, C) = S(f, C) .Merge. S(f, Bi);
      }
      
  5. Zum Schluss wird der Deklarationssatz als Ergebnis der Namensauflösung (§ 10.2 / 7) zurückgegeben.

    return S(f, C).Δ;
    
  6. die Zusammenführung zwischen zwei Nachschlagsätzen ( δ 1 , Σ 1 ) und ( δ 2 , Σ 2 ) ist definiert als (§10.2 / 6) :

      .
    • wenn jede Klasse in σ 1 eine Basisklasse von mindestens einer Klasse in σ 2 ist, Rückkehr ( δ 2 , Σ 2 ).
      (Ähnlich für das umgekehrte.)
    • sonst, wenn δ 1 Δ 2 zurückkehren ( mehrdeutig , Σ 1 Σ 2 ).
    • Andernfalls zurückkehren ( δ 1 , Σ 1 Σ 2 )

      function Merge ( (Δ1, Σ1), (Δ2, Σ2) ) {
      
         function IsBaseOf(Σp, Σq) {
           for (B1 in Σp) {
             if (not any(B1 is base of C for (C in Σq)))
               return false;
           }
           return true;
         }
      
         if      (Σ1 .IsBaseOf. Σ2) return (Δ2, Σ2);
         else if (Σ2 .IsBaseOf. Σ1) return (Δ1, Σ1);
         else {
            Σ = Σ1 union Σ2;
            if (Δ1 != Δ2)
              Δ = ambiguous; 
            else
              Δ = Δ1;
            return (Δ, Σ);
         }
      }
      

  7. zum Beispiel (§10.2 / 10) ,

    generasacodicetagpre.

    wir berechnen das

    generasacodicetagpre.

    und

    generasacodicetagpre.

Andere Tipps

Einige detaillierte Beschreibung mit Code.

vtable undvptr

vtable

virtuelle Funktionen

Wenn Sie darüber reden G++ eine vtable (Virtuelle Methodentabelle) verwendet wird, können Sie genauere Details erhalten Hier.Ich bin mir nicht sicher, ob jeder C++-Compiler den gleichen Ansatz verwendet, aber ich würde ja sagen

Wenn ein Verfahren einer Basisklasse virtuell ist, nennt jeder Anruf durch Basis oder abgeleitete Zeiger / Referenz die entsprechende Methode (der am weitesten entfernte Vererbungstaum).Wenn die Methode virtuell deklariert wurde, können Sie es nicht später nicht mehr haben: Wenn Sie ihn in abgeleiteten Klassen nicht ändern, ändern Sie nichts.

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