Reihenfolge der Methodenauflösung in C++
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.)
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:
- .
-
Angenommen, Sie möchten eine Funktion
f in der Klasse c nachschlagen. -
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.
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) = (Δ, Σ);
wenn
- .
-
berechnen s (f, b i ) wobei
b i eine Basisklasse von istc
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);
}
Zum Schluss wird der Deklarationssatz als Ergebnis der Namensauflösung (§ 10.2 / 7) zurückgegeben.
return S(f, C).Δ;
die Zusammenführung zwischen zwei Nachschlagsätzen (
- .
- wenn jede Klasse in σ 1 eine Basisklasse von mindestens einer Klasse in σ 2 ist, Rückkehr (
δ 2 ,
(Ähnlich für das umgekehrte.)
Andernfalls zurückkehren (
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 (Δ, Σ);
}
}
zum Beispiel (§10.2 / 10) ,
generasacodicetagpre.wir berechnen das
generasacodicetagpre.und
generasacodicetagpre.Andere Tipps
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.