方法分辨率在C ++中排序
题
考虑以下类层次结构:
- 基类对象具有虚拟方法foo()
- 具有多重继承的任意层次结构(虚拟和非虚拟);每个班级是对象的亚型;其中一些覆盖foo(),有些不
- 来自此层次结构的类x,而不是覆盖foo()
如何在C ++类X类对象上呼叫foo()的调用时执行哪种方法?
(我正在寻找算法,而不是任何特定的情况。)
解决方案
在python等c ++中没有mro。如果方法模糊,则是一个编译时错误。方法是否虚拟或不影响它,但虚拟继承。
该算法在C ++标准§[class.member.lookup](10.2)中描述。基本上它将在超类图中找到最接近的明确实现。该算法如下所示:
-
假设您希望在类 c 中查找一个函数 f 。
-
我们定义了查找集 s(f,c)是一对集(δ,σ)代表所有可能性。 (§10.2/ 3)
-
设置Δ称为声明集,基本上是可能的 f 。
-
set σ称为 subObject set ,其中包含找到这些 f s的类。
让 s(f,c)包括所有 f 在 c 中直接定义(或using
-ed),如果有的话(§10.2/ 4):
Δ = {f in C};
if (Δ != empty)
Σ = {C};
else
Σ = empty;
S(f, C) = (Δ, Σ);
如果 s(f,c)为空(§10.2/ 5),
-
计算 s(f,b i )其中 b i 是一个基类 c ,适用于所有 i 。
将每个 s(f,b i )逐一合并为 s(f,c)。
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);
}
最后,声明集作为名称解析(§10.2/ 7)。
return S(f, C).Δ;
两个查找集之间的合并(Δ 1 ,σ 1 )和(δ 2 ,σ 2 )定义为(§10.2/ 6):
- 如果σ 1 中的每个类是σ 2 中至少一个类的基类,返回(δ 2 ,σ 2 )。
(类似于反向。)
如果δ 1 ≠Δ 2 ,返回(模棱两可。 σ 1 ∪σ 2 )。
否则,返回(Δ 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 (Δ, Σ);
}
}
例如(§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();
}
};
.
我们计算该
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.
.
和
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.
. 其他提示
如果您正在谈论G++
A VTable(虚拟方法表),则可以获得更多特定的详细信息这里。不确定每个C ++编译器是否使用相同的方法,但我会说是
如果基类的方法是虚拟的,则通过基础或派生指针/引用对其进行每个调用将调用适当的方法(最遍历继承树的那个)。如果已声明该方法虚拟,则稍后您无法使用它:在派生类中声明它虚拟(或不)不会更改任何内容。