Risoluzione di sovraccarico C ++

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

Non dovrebbe la risoluzione di sovraccarico del compilatore capire quale metodo sto parlando?

Sto usando Microsoft VS 2005. (Nota: l’uso di virtual 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; } 

I due “sovraccarichi” non sono nella stessa portata. Per impostazione predefinita, il compilatore considera solo l’ambito del nome più piccolo ansible finché non trova una corrispondenza di nome. La corrispondenza degli argomenti viene eseguita in seguito . Nel tuo caso ciò significa che il compilatore vede B::DoSomething . Quindi cerca di far corrispondere l’elenco degli argomenti, che fallisce.

Una soluzione potrebbe essere quella di ridurre il sovraccarico dall’ambito di A a B :

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

La risoluzione del sovraccarico è una delle parti più brutte del C ++

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

Può essere superato utilizzando A :: DoSomething in class 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 evitare di essere preso in errore ereditando da classi di base lontane per errore.

Per aggirare il problema, è necessario indicare al compilatore quale metodo si desidera chiamare posizionando un object A :: DoSomething nella class B.

Vedi questo articolo per una rapida e semplice panoramica di questo comportamento.

La presenza di un metodo in una class derivata nasconde tutti i metodi con lo stesso nome (indipendentemente dai parametri) nelle classi base. Questo è 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)); 

più tardi qualcuno cambia class A:

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

ora improvvisamente:

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

In altre parole, se non funziona in questo modo, un cambiamento non correlato in una class che non controlli (A), potrebbe influenzare in silenzio il funzionamento del codice.

Questo ha qualcosa a che fare con il modo in cui funziona la risoluzione dei nomi. Fondamentalmente, per prima cosa troviamo lo scope da cui proviene il nome, quindi raccogliamo tutti gli overload per quel nome in quell’ambito. Tuttavia, l’ambito nel tuo caso è di class B, e in class B, B :: DoSomething nasconde A :: DOSomething:

3.3.7 Nascondere il nome [basic.scope.hiding]

… [omissis] …

3 In una definizione di funzione membro, la dichiarazione di un nome locale nasconde la dichiarazione di un membro della class con lo stesso nome; vedi basic.scope.class . La dichiarazione di un membro in una class derivata ( class.derived ) nasconde la dichiarazione di un membro di una class base con lo stesso nome; vedi class.member.lookup .

A causa del nome nascosto, A :: DoSomething non è nemmeno considerato per la risoluzione di sovraccarico

Quando definisci una funzione in una class derivata, nasconde tutte le funzioni con quel nome nella class base. Se la funzione della class base è virtuale e ha una firma compatibile, la funzione della class derivata sovrascrive anche la funzione della class base. Tuttavia, ciò non influisce sulla visibilità.

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

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

Non è sovraccarico! È NASCOSTO!

Quando si cerca l’albero di ereditarietà per la funzione da utilizzare, C ++ utilizza il nome senza argomenti, una volta trovata la definizione che si ferma, quindi esamina gli argomenti. Nell’esempio fornito, si ferma nella class B. Per essere in grado di fare ciò che si sta cercando, la class 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 sottoclass (ma con una firma diversa). Puoi renderlo visibile usando l’istruzione using, come in A :: DoSomething ();