Domanda

Questa domanda ha già una risposta qui:

Dato il seguente esempio, perché devo utilizzare esplicitamente l'istruzione b->A::DoSomething() piuttosto che solo b->DoSomething()?

La risoluzione dell'overload del compilatore non dovrebbe capire di quale metodo sto parlando?

Sto usando Microsoft VS 2005.(Nota:usare il virtuale non aiuta in questo caso.)

class A
{
  public:
    int DoSomething() {return 0;};
};

class B : public A
{
  public:
    int DoSomething(int x) {return 1;};
};

int main()
{
  B* b = new B();
  b->A::DoSomething();    //Why this?
  //b->DoSomething();    //Why not this? (Gives compiler error.)
  delete b;
  return 0;
}
È stato utile?

Soluzione

I due “sovraccarico” non rientrano nello stesso ambito.Per impostazione predefinita, il compilatore considera solo l'ambito del nome più piccolo possibile finché non trova una corrispondenza del nome.La corrispondenza degli argomenti è stata eseguita dopo.Nel tuo caso questo significa che il compilatore vede B::DoSomething.Quindi tenta di far corrispondere l'elenco degli argomenti, ma fallisce.

Una soluzione potrebbe essere quella di eliminare il sovraccarico da A in Bambito di:

class B : public A {
public:
    using A::DoSomething;
    // …
}

Altri suggerimenti

La risoluzione dell'overload è una delle parti più brutte del C++

Fondamentalmente il compilatore trova una corrispondenza del nome "DoSomething(int)" nell'ambito di B, vede che i parametri non corrispondono e si ferma con un errore.

Può essere superato utilizzando A::DoSomething in classe B

class A  
{  
  public:  
    int DoSomething() {return 0;}
};  

class B : public A  
{  
  public:  
    using A::DoSomething;
    int DoSomething(int x) {return 1;} 
};   


int main(int argc, char** argv)
{
  B* b = new B();  
  // b->A::DoSomething();    // still works, but...
  b->DoSomething();    // works now too
  delete b;  
  return 0;
}

No, questo comportamento è presente per garantire che non venga scoperto per errore l'ereditarietà da classi base lontane.

Per aggirare il problema, devi dire al compilatore quale metodo vuoi chiamare inserendo un using A::DoSomething nella classe B.

Vedere Questo articolo per una panoramica rapida e semplice di questo comportamento.

La presenza di un metodo in una classe derivata nasconde tutti i metodi con lo stesso nome (indipendentemente dai parametri) nelle classi base.Questo viene fatto per evitare problemi come questo:

class A {} ;
class B :public A
{
    void DoSomething(long) {...}
}

B b;
b.DoSomething(1);     // calls B::DoSomething((long)1));

poi qualcuno cambia classe A:

class A
{
    void DoSomething(int ) {...}
}

ora all'improvviso:

B b;
b.DoSomething(1);     // calls A::DoSomething(1);

In altre parole, se non funzionasse in questo modo, una modifica non correlata in una classe che non controlli (A) potrebbe influenzare silenziosamente il funzionamento del tuo codice.

Questo ha qualcosa a che fare con il modo in cui funziona la risoluzione dei nomi.Fondamentalmente, innanzitutto troviamo l'ambito da cui deriva il nome, quindi raccogliamo tutti gli sovraccarichi per quel nome in quell'ambito.Tuttavia, l'ambito nel tuo caso è la classe B e nella classe B, B::DoSomething nasconde A::DOqualcosa:

3.3.7 Nascondere il nome [basic.scope.hiding]

...[taglia]...

3 In una definizione della funzione membro, la dichiarazione di un nome locale nasconde la dichiarazione di un membro della classe con lo stesso nome;Vedere basic.scope.class.La dichiarazione di un membro in una classe derivata (classe.derivato) nasconde la dichiarazione di un membro di una classe base con lo stesso nome;Vedere ricerca.membro.della.classe.

A causa dell'occultamento del nome, A::DoSomething non viene nemmeno considerato per la risoluzione dell'overload

Quando definisci una funzione in una classe derivata, nasconde tutte le funzioni con quel nome nella classe base.Se la funzione della classe base è virtuale e ha una firma compatibile, anche la funzione della classe derivata sovrascrive la funzione della classe base.Ciò però non pregiudica la visibilità.

Puoi rendere visibile la funzione della classe base con una dichiarazione using:

class B : public A  
{  
  public:  
    int DoSomething(int x) {return 1;};  
    using A::DoSomething;
};  

Non è un sovraccarico!Questo è NASCONDERSI!

Durante la ricerca nell'albero dell'ereditarietà della funzione da utilizzare, il C++ utilizza il nome senza argomenti, una volta trovata una definizione si ferma, quindi esamina gli argomenti.Nell'esempio riportato si ferma alla classe B.Per poter fare ciò che cerchi, la classe B dovrebbe essere definita in questo modo:

class B : public A  
{  
  public:
    using A::DoSomething;
    int DoSomething(int x) {return 1;};  
}; 

La funzione è nascosta dalla funzione con lo stesso nome nella sottoclasse (ma con una firma diversa).Puoi mostrarlo utilizzando l'istruzione using, come nell'utilizzo di A::DoSomething();

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top