C++中的方法解析顺序

Kos*_*Kos 10 c++ multiple-inheritance virtual-inheritance method-resolution-order

考虑以下类层次结构:

  • 基类使用虚方法foo()的对象
  • 具有多重继承的任意层次结构(虚拟和非虚拟); 每个类都是Object的子类型; 其中一些覆盖了foo(),有些则没有
  • 来自此层次结构的类X,不覆盖foo()

如何确定在C++中对类X的对象调用foo()时将执行哪个方法?

(我正在寻找算法,而不是任何特定情况.)

ken*_*ytm 24

像Python这样的C++中没有MRO.如果方法不明确,则是编译时错误.方法是否为虚拟不会影响它,但虚拟继承会影响它.


该算法在C++标准§[class.member.lookup](10.2)中描述.基本上它会在超类图中找到最接近的明确实现.该算法的工作方式如下:

  1. 假设您要在C类中查找函数f.

  2. 我们将查找集 S(f,C)定义为表示所有可能性的一对集合(Δ,Σ). (§10.2/ 3)

    • 集合Δ称为声明集,基本上是所有可能的f.

    • 集合Σ称为子对象集合,它包含找到这些f的类.

  3. S(F,C)包括所有˚F直接定义(或using在-ed)Ç,如果有的话(§10.2/ 4) :

    ? = {f in C};
    if (? != empty)
      ? = {C};
    else
      ? = empty;
    S(f, C) = (?, ?);
    
    Run Code Online (Sandbox Code Playgroud)
  4. 如果S(f,C)为空(§10.2/ 5),

    • 计算S(f,B i)其中B iC的基类,对于所有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);
      }
      
      Run Code Online (Sandbox Code Playgroud)
  5. 最后,声明集作为名称解析的结果返回(第10.2/7节).

    return S(f, C).?;
    
    Run Code Online (Sandbox Code Playgroud)
  6. 两个查找向上集(之间合并Δ 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 (?, ?);
         }
      }
      
      Run Code Online (Sandbox Code Playgroud)

例如(§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();
   }
};
Run Code Online (Sandbox Code Playgroud)

我们计算一下

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.
Run Code Online (Sandbox Code Playgroud)

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.
Run Code Online (Sandbox Code Playgroud)