9

consideri il seguente gerarchia di classi:Metodo C++

  • classe base Object con un metodo virtuale foo()
  • una gerarchia arbitrario con ereditarietà multipla (virtuale e non virtuale); ogni classe è un sottotipo di Oggetto; alcuni di loro sovrascrivono foo(), altri no
  • una classe X da questa gerarchia, non ignorando foo()

Come determinare quale metodo verrà eseguito su una chiamata di foo() su un oggetto della classe X in C++?

(sto cercando l'algoritmo, non ogni caso specifico.)

+3

Ragazzi, non sta chiedendo di un tavolo virtuale. Sta chiedendo come il compilatore sceglie quale 'foo' è chiamato. – GManNickG

risposta

22

Non c'è MRO in C++ come Python. Se un metodo è ambiguo, si tratta di un errore in fase di compilazione. Se un metodo è virtuale o meno non lo influenza, ma l'ereditarietà virtuale sarà.


L'algoritmo è descritto nella C++ norma § [class.member.lookup] (10.2). Fondamentalmente troverà l'implementazione non ambigua più vicina nel grafico della superclasse. L'algoritmo funziona così:

  1. Si supponga di voler ricercare una funzione f in classe C .

  2. definiamo un look-up impostatoS (f, C) essendo una coppia di gruppi (Δ, Σ) rappresenta tutte le possibilità. (§ 10,2/3)

    • Il set Δ è chiamato dichiarazione impostato, che è sostanzialmente tutti i possibili f 's.

    • Il set Σ è chiamato subobject impostato, che contengono le classi che questi f 's si trovano.

  3. Let S (f, C) comprendono tutti f direttamente definito (o using -ed) in C eventuali (§ 10.2/4):

    Δ = {f in C}; 
    if (Δ != empty) 
        Σ = {C}; 
    else 
        Σ = empty; 
    S(f, C) = (Δ, Σ); 
    
  4. If S (f, C) è vuota (§ 10,2/5),

    • Calcola S (f, B i) dove B i è una classe base di C, per tutti i.

    • unione ciascuno S (f, B i) in S (f, C) uno per 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. Infine il set dichiarazione viene restituito come risultato della risoluzione dei nomi (§ 10,2/7).

    return S(f, C).Δ; 
    
  6. L'unione tra due insiemi di look-up (Δ, Σ) e (Δ, Σ) è definito come (§ 10,2/6):

    • Se ogni classe Σ è una classe base di almeno una classe in Σ, ritorno (Δ, Σ).
      (simili per la retromarcia.)
    • Altrimenti se ΔΔ, ritorno (ambiguo, ΣΣ).
    • Altrimenti, ritorno (Δ, ΣΣ)

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

Per esempio (§ 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(); 
    } 
}; 

calcoliamo che

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. 

e

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

Grazie per la descrizione dettagliata :) Questo è esattamente ciò di cui avevo bisogno. – Kos

0

Se si sta parlando di un G++ vtable (virtuale Method Table) viene utilizzato, è possibile ottenere dettagli più specifici here. Non sono sicuro che ogni compilatore C++ usi lo stesso approccio, ma direi di si

0

Se un metodo di una classe base è virtuale, ogni chiamata ad esso attraverso la base o derivato puntatore/riferimento chiamerà il metodo appropriato (quello più lontano giù l'albero dell'eredità). Se il metodo è stato dichiarato virtuale, non è possibile averlo in un secondo momento: dichiararlo virtuale (o meno) nelle classi derivate non cambierà nulla.

+0

Sì, ma quello che sto chiedendo è in sostanza "il metodo appropriato", che non è banale se l'albero di ereditarietà è, beh, non un albero. Python ha un bel documento su come lo fanno: http://www.python.org/download/releases/2.3/mro/; Sto cercando di trovare una spiegazione simile per C++. – Kos