9

Consideremos el siguiente jerarquía de clases:Método en C++

clase Object
  • base con un foo método virtual()
  • una jerarquía arbitraria con la herencia múltiple (virtual y no virtual); cada clase es un subtipo de Objeto; algunos de ellos anulan foo(), otros no
  • una clase X a partir de esta jerarquía, no anulando foo()

Cómo determinar qué método se va a ejecutar en una llamada de foo() en una objeto de clase X en C++?

(Busco el algoritmo, no todo caso específico.)

+3

Chicos, él no está preguntando acerca de una tabla virtual. Él pregunta cómo el compilador elige a qué 'foo' se llama. – GManNickG

Respuesta

22

No hay MRO en C++ como Python. Si un método es ambiguo, es un error en tiempo de compilación. Si un método es virtual o no, no lo afecta, pero la herencia virtual lo hará.


El algoritmo se describe en el C++ estándar § [class.member.lookup] (10.2). Básicamente, encontrará la implementación no ambigua más cercana en el gráfico de la superclase. El algoritmo funciona así:

  1. Supongamos que desea buscar una función f en la clase C .

  2. definimos un look-up estableceS (f, C) ser un par de conjuntos (Δ, Σ) que representa todas las posibilidades. (§ 10,2/3)

    • El conjunto Δ se llama la declaración establece, que es básicamente todas las posibles f 's.

    • El conjunto Σ se llama el subobjeto establece, que contienen las clases que se encuentre este f 's.

  3. Vamos S (f, C) incluyen todos f directamente definido (o using -ed) en C, si cualquier (§ 10.2/4):

    Δ = {f in C}; 
    if (Δ != empty) 
        Σ = {C}; 
    else 
        Σ = empty; 
    S(f, C) = (Δ, Σ); 
    
  4. If S (F, C) está vacía (§ 10,2/5),

    • Compute S (f, B i) donde B i es una clase base de C, para todos i.

    • Combinar cada S (f, B i) en S (F, C) uno por uno.

      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. Finalmente el conjunto declaración se obtiene como resultado de la resolución de nombres (§ 10.2/7).

    return S(f, C).Δ; 
    
  6. La fusión entre dos conjuntos de consulta (Δ, Σ) y (Δ, Σ) se define como (§ 10.2/6):

    • Si cada clase en Σ es una clase base de al menos una clase en Σ, de retorno (Δ, Σ)
      (similares a la inversa.)
    • Porque si ΔΔ, de retorno ( ambigua, ΣΣ).
    • De lo contrario, de retorno (Δ, ΣΣ)

      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 (Δ, Σ); 
          } 
      } 
      

Por ejemplo (§ 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(); 
    } 
}; 

calculamos 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. 

y

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. 
+0

http://www.csci.csusb.edu/dick/c++std/cd2/derived.html#class.member.lookup – pascal

+0

Gracias por la descripción detallada :) Eso es exactamente lo que necesitaba. – Kos

0

Si usted está hablando de un G++ vtable (método virtual Tabla) se utiliza, puede obtener detalles más específicos here. No estoy seguro si cada compilador de C++ utiliza el mismo enfoque, pero yo diría que sí

0

Si un método de una clase base es virtual, cada llamada a la misma a través de la base o derivado puntero/referencia llamará el método apropiado (El que está más abajo el árbol de herencia). Si el método fue declarado virtual, no puede tenerlo de otra manera más tarde: declararlo virtual (o no) en las clases derivadas no cambiará nada.

+0

Sí, pero lo que estoy preguntando es básicamente cuál es "el método apropiado", que no es trivial si el árbol de herencia es, bueno, no un árbol. Python tiene un buen documento sobre cómo lo hacen: http://www.python.org/download/releases/2.3/mro/; Estoy tratando de encontrar una explicación similar para C++. – Kos