Commande de résolution de la méthode en C ++
Question
Considérez la hiérarchie de classe suivante:
- objet de la classe de base avec une méthode virtuelle FOO ()
- une hiérarchie arbitraire à héritage multiple (virtuel et non virtuel);Chaque classe est un sous-type d'objet;certains d'entre eux remplacent foo (), certains ne
- Une classe X de cette hiérarchie, ne pas remplacer foo ()
Comment déterminer quelle méthode sera exécutée sur un appel de FOO () sur un objet de classe X en C ++?
(Je cherche l'algorithme, pas un cas particulier.)
La solution
Il n'y a pas de MRO en C ++ comme Python. Si une méthode est ambiguë, c'est une erreur de compilation. Si une méthode est virtuelle ou non ne l'affecte pas, mais virtuel héritage sera.
L'algorithme est décrit dans la norme C ++ § [Class.Member.Lookup] (10.2). Fondamentalement, il trouvera la mise en œuvre sans ambiguïté la plus proche du graphique de la superclasse. L'algorithme fonctionne comme ceci:
-
supposons que vous voulez rechercher une fonction
f en classe C . -
Nous définissons un ensemble look-up s (f, c) étant une paire d'ensembles ( δ "fort > Σ ) représentant toutes les possibilités. (§10.2 / 3)
-
L'ensemble
δ est appelé le jeu de déclaration , qui est fondamentalement tout le possible f . -
L'ensemble σ est appelé le jeu sous-box , qui contiennent les classes que ces
f sont trouvés.
laisse s (f, c) inclure tous les
Δ = {f in C};
if (Δ != empty)
Σ = {C};
else
Σ = empty;
S(f, C) = (Δ, Σ);
si
-
calcule s (f, b i ) où
b i est une classe de base de C , pour tous i .
fusionner chaque s (f, b i ) dans s (f, c) un par un.
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);
}
Enfin, l'ensemble de déclaration est renvoyé à la suite de la résolution de noms (§10.2 / 7) .
.return S(f, C).Δ;
la fusion entre deux ensembles de recherche ( δ 1 , σ 1
) et ( δ 2 , σ 2 ) est défini comme (§10.2 / 6) :- Si chaque classe in
σ 1 est une classe de base d'au moins une classe dans σ 2 , Retour ( δ 2 , σ 2 ).
(Semblable pour l'inverse.)
sinon, renvoyez ( δ 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 (Δ, Σ);
}
}
Par exemple (§10.2 / 10) ,
struct V { int f(); };
struct W { int g(); };
struct B : W, virtual V { int f(); int g(); };
struct C : W, virtual V { };
struct D : B, C {
void glorp () {
f();
g();
}
};
Nous calculons que
S(f, D) = S(f, B from D) .Merge. S(f, C from D)
= ({B::f}, {B from D}) .Merge. S(f, W from C from D) .Merge. S(f, V)
= ({B::f}, {B from D}) .Merge. empty .Merge. ({V::f}, {V})
= ({B::f}, {B from D}) // fine, V is a base class of B.
et
S(g, D) = S(g, B from D) .Merge. S(g, C from D)
= ({B::g}, {B from D}) .Merge. S(g, W from C from D) .Merge. S(g, V)
= ({B::g}, {B from D}) .Merge. ({W::g}, {W from C from D}) .Merge. empty
= (ambiguous, {B from D, W from C from D}) // the W from C is unrelated to B.
Autres conseils
Si vous parlez de G++
, une table à fourchette ( la table de la méthode virtuelle ) est utilisée, vous pouvez obtenir plus de détails spécifiques
Si une méthode d'une classe de base est virtuelle, chaque appel à celui-ci à travers la base ou le pointeur dérivé / référence appellera la méthode appropriée (la fois la plus éloignée de l'arborescence d'héritage).Si la méthode a été déclarée virtuelle, vous ne pouvez pas l'avoir autrefois: la déclarant virtuelle (ou non) dans des classes dérivées ne changera rien.