Domanda

Si prega di considerare il seguente codice:

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 classe di base, piuttosto che upcasting l'argomento del f()? C'è un modo che io possa ottenere il comportamento desiderato?

È stato utile?

Soluzione

La scelta di quale versione di f a chiamata viene effettuata, cercando in in fase di compilazione tipo di parametro. Il tipo run-time non è considerato per questo la risoluzione dei nomi. Poiché b1 è di tipo Bbase*, tutti i membri del Bbase sono considerati; quella che prende un A2* è la miglior partita, quindi questo è quello che viene chiamato.

Altri suggerimenti

"... sceglie di risolvere la chiamata di funzione sull'ultima riga dal upcasting il puntatore this dell'oggetto alla classe di base ..." . Di cosa stai parlando? In tutte le chiamate, il tipo di puntatore oggetto è Bbase * e le funzioni delle chiamate risolvere appartenere a uno Bbase o ai suoi discendenti. Il compilatore non fa mai alcun upcasting al fine di risolvere le chiamate. In realtà, le prime due chiamate richiedono downcasting al fine di chiamare il corretto overrider, dal momento che l'overrider appartiene alla classe situato più in basso nella gerarchia. Per quanto riguarda gli ultimi due inviti - sono spediti nella classe Bbase tramite un puntatore di tipo Bbase *. I tipi corrispondono esattamente, nessuna colata di alcun genere avviene.

Come per la risoluzione di sovraccarico ... risoluzione sovraccarico è un processo di compilazione, che si basa sui tipi statici degli argomenti e le file dei possibili conversioni. È fornito un argomento di tipo A2 *. Il candidato f(A2 *) abbinato il vostro argomento esattamente . Il candidato f(A1 *) richiede una conversione extra da A2 * a A1 *. Il candidato che corrisponde esattamente è considerato uno migliore, quindi vince la risoluzione di sovraccarico. Semplice.

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

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

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

Usa un cast alla classe di base per ottenere il comportamento desiderato.

Quando si ignora una funzione virtuale, non si sovrascrivere le funzioni sovraccarichi con lo stesso nome. Sono funzioni diverse (e hanno diverse voci nel vtable).

I tuoi sovraccarichi in Bbase per Abase e A2 sono nascosti in B1. Forse si può 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);  
};
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top