2010-06-23 12 views
7

Si prega di considerare il seguente codice:overload chiamata di funzione virtual risoluzione

class Abase{}; 
class A1:public Abase{}; 
class A2:public A1{}; 
//etc 

class Bbase{ 
public: 
    virtual void f(Abase* a); 
    virtual void f(A1* a); 
    virtual void f(A2* a); 
}; 

class B1:public Bbase{ 
public: 
    void f(A1* a); 
}; 

class B2:public Bbase{ 
public: 
    void f(A2* a); 
}; 

int main(){ 
    A1* a1=new A1(); 
    A2* a2=new A2(); 
    Bbase* b1=new B1(); 
    Bbase* b2=new B2(); 
    b1->f(a1); // calls B1::f(A1*), ok 
    b2->f(a2); // calls B2::f(A2*), ok 
    b2->f(a1); // calls Bbase::f(A1*), ok 
    b1->f(a2); // calls Bbase::f(A2*), no- want B1::f(A1*)! 
} 

mi interessa sapere il motivo per cui C++ sceglie di risolvere la chiamata di funzione sull'ultima riga dal upcasting il puntatore this dell'oggetto alla base classe, piuttosto che upcasting l'argomento di f()? C'è un modo per ottenere il comportamento che voglio?

risposta

10

La scelta della versione di f da chiamare viene effettuata osservando il tipo compile-time del parametro. Il tipo di runtime non è considerato per questa risoluzione del nome. Poiché b1 è di tipo Bbase*, vengono considerati tutti i membri di Bbase; quello che prende un A2* è la migliore corrispondenza, quindi è quello che viene chiamato.

+0

Grazie - a quanto ho capito il punto è che la risoluzione di quale virtuale f() chiamare avviene al momento della compilazione, in base all'argomento fornito a f(). Quindi, sull'ultima riga, il compilatore ha già deciso che f (A2 *) sarà chiamato. La versione di f (A2 *) richiamata dipende dal tipo di runtime puntato. Qui, poiché la classe B1 non ha la funzione f (A2 *), viene chiamata la versione della classe base. – stw

+0

@stw, assolutamente corretto. –

1
b1->f(static_cast<A1*>(a2)); 

Questo dovrebbe forzare il compilatore a utilizzare il metodo di sovraccarico con parametro di tipo A1.

+0

un dynamic_cast è più appropriato, credo. – Jason

+2

@Jason: un 'static_cast' in una classe base va bene. –

+2

Penso che un cast statico sia OK, come noi a) sappiamo che il cast è corretto e b) è un upcast, che è sicuro. –

0

Si chiama Nascondere nome. Ogni f si dichiara in una classe derivata le ombre ogni possibile f in una qualsiasi delle sue classi base.

Utilizzare un cast per la classe base per ottenere il comportamento desiderato.

Quando si esegue l'override di una funzione virtuale, non si esegue l'override delle funzioni sovraccaricate con lo stesso nome. Sono funzioni diverse (e hanno voci diverse nel vtable).

+0

No, il problema qui è il contrario. Sarebbe il nascondiglio del nome se si chiamasse attraverso un puntatore a una classe più derivata che nasconde i metodi dalla classe base. –

+0

ooops hai ragione, ho letto troppo veloce. –

2

"... sceglie di risolvere la chiamata di funzione sull'ultima riga aggiornando il puntatore dell'oggetto alla classe di base ...". Di cosa stai parlando? In tutte le chiamate, il tipo di puntatore dell'oggetto è Bbase * e le funzioni che le chiamate risolvono appartengono a Bbase o ai suoi discendenti. Il compilatore non esegue mai alcun upcasting per risolvere le tue chiamate. In effetti, le prime due chiamate richiedono downcasting per chiamare l'overrider appropriato, poiché lo overrider appartiene alla classe situata più in basso nella gerarchia. Per quanto riguarda le ultime due chiamate, vengono inviate nella classe Bbase tramite un puntatore del tipo Bbase *. I tipi corrispondono esattamente, nessuna fusione di alcun tipo ha luogo.

Per quanto riguarda la risoluzione di sovraccarico ... La risoluzione di sovraccarico è un processo di compilazione del tempo, che si basa sui tipi statici degli argomenti e sui ranghi delle possibili conversioni. Hai fornito un argomento di tipo A2 *. Il candidato f(A2 *) ha confrontato il tuo argomento con precisione. Il candidato f(A1 *) richiede una conversione aggiuntiva da A2 * a A1 *. Il candidato che corrisponde esattamente è considerato migliore, quindi vince la risoluzione di sovraccarico. Semplice.

+0

Scusa- ho usato il termine "upcasting" nel modo sbagliato. Quello che intendevo era perché l'oggetto è trattato come un BBase qui. La risposta (come dici tu) è che la risoluzione del sovraccarico avviene al momento della compilazione, e al momento della compilazione l'oggetto è un BBase, quindi il compilatore sceglie f (A2 *). – stw

0

I sovraccarichi in Bbase per Abase e A2 sono nascosti in B1. Forse è possibile aggirare il problema in questo modo:

class Bbase{ 
public: 
    inline void f(Abase* a) { f_(a); } 
    inline void f(A1* a) { f_(a); } 
    inline void f(A2* a) { f_(a); } 
protected: 
    virtual void f_(Abase* a); 
    virtual void f_(A1* a); 
    virtual void f_(A2* a); 
}; 

class B1:public Bbase{ 
protected: 
    void f_(A1* a); 
}; 

class B2:public Bbase{ 
protected: 
    void f_(A2* a); 
}; 

o con un modello in Bbase:

class Bbase{ 
public: 
    template<class myA> 
    inline void f(myA* a) { f_(a); } 
protected: 
    virtual void f_(Abase* a); 
    virtual void f_(A1* a); 
    virtual void f_(A2* a); 
}; 
Problemi correlati