2012-04-16 9 views
6

Facendo riferimento un altro so questionconfusione per quanto riguarda il nome nascondiglio e funzioni virtuali

Si consideri il codice:

class Base { 
public: 
    virtual void gogo(int a){ 
     printf(" Base :: gogo (int) \n"); 
    }; 

    virtual void gogo(int* a){ 
     printf(" Base :: gogo (int*) \n"); 
    }; 
}; 

class Derived : public Base{ 
public: 
    virtual void gogo(int* a){ 
     printf(" Derived :: gogo (int*) \n"); 
    }; 
}; 

int main(){ 

    // 1)  
    Derived * obj = new Derived ; 
    obj->gogo(7); // this is illegal because of name hiding 


    // 2)  
    Base* obj = new Derived ; 
    obj->gogo(7); // this is legal 
} 

Per il caso 2)

La chiamata obj->gogo(7) viene risolto in fase di esecuzione.

Poiché obj->gogo(7) è legale. Sembra implicare che vtable di Derived contenga ptr a virtual void gogo(int a) che avrebbe dovuto essere nascosto.

La mia confusione è, dal nome nascondiglio provoca caso 1) a essere illegale, allora come la chiamata in 2) viene risolto in fase di esecuzione

a) Se vtable di Derivato contiene puntatore a gogo (int).

b) Se a) non è True, la risoluzione delle chiamate per le funzioni virtuali passa a vtable della classe base.

+0

@AndersK La funzione 'Base :: gogo (int)' è effettivamente nascosta da 'Derived :: gogo (int *)'. Ma l'istruzione 'using Base :: gogo;' nella classe 'Derivata' risolverebbe questo particolare problema. –

+0

@ Michaelison, sì, ho visto il mio errore. –

risposta

0

Poiché hai dichiarato il secondo obj come Base*, il vtable fornisce tutti i metodi di Base. Sebbene per i metodi virtuali che sono stati sostituiti da Derived, vengono chiamate le versioni sovrascritte, gli altri metodi (o overload di metodi) sono ancora quelli dichiarati in Base.

Tuttavia, se dichiarato il puntatore come Derived*, vtable darà i metodi di Derived, nascondendo quelli che avevano lo stesso nome in Base. Quindi, obj->gogo(7); non funzionerà. Allo stesso modo, questo è anche illegale:

Base* obj = new Derived(); 

// legal, since obj is a pointer to Base, it contains the gogo(int) method. 
obj->gogo(7); 

// illegal, it has been cast into a pointer to Derived. gogo(int) is hidden. 
(reinterpret_cast<Derived*>(obj))->gogo(7); 

Questo è legale:

Derived* obj = new Derived ; 
obj->Base::gogo(7); // legal. 

Vedi here.

+0

Non sarebbe 'dynamic_cast' più appropriato nel tuo esempio? – Griwes

+0

Per quanto ne so, questo non ha nulla a che vedere con la reale ricerca di 'vtable', ma con tipizzazione statica e nascondigli di nomi (vedere la risposta di Bo Persson). Se type è 'Derived', si applica la semantica, se type è' Base', si applica l'altra semantica. – KillianDS

5

Si stanno confondendo chiamate di funzioni virtuali e risoluzione di sovraccarico.

Tutte le classi derivate dispongono di vtables contenenti tutte le funzioni virtuali, dalla classe base e eventuali altre funzioni virtuali proprie. Questo è usato per risolvere le chiamate al runtime, come nel tuo caso 2).

Nel caso 1) si riceve un errore dalla risoluzione di sovraccarico a tempo di compilazione. A causa del nome nascosto, la classe Derived ha una sola funzione callable. La tua unica scelta è quella di chiamare quella funzione, con un int*.

+0

Nessun errore. Poiché entrambi i sovraccarichi di 'gogo()' sono stati dichiarati in 'Base', ignorando uno non si nasconde l'altro. –

+0

questo spiega il comportamento sopra. Lo standard C++ (o qualsiasi libro/documento) descrive la stessa cosa. Voglio dire è possibile ottenere una citazione. – fizzbuzz

+0

@fizzbuzz - Un nome dichiarato in un ambito interno nasconde sempre un nome in un ambito esterno. In questo caso la classe derivata è l'ambito interno e la classe base l'ambito esterno. –

1

In pratica, l'overloading delle funzioni si verifica solo quando le funzioni con lo stesso nome sono definite nello stesso ambito. Ora, la classe Base ha il proprio ambito e la classe derivata ha il suo.

Quindi, quando non si ridefinisce una funzione nella classe derivata e si chiama tale funzione, il compilatore controlla l'ambito di derivata, scopre che non esiste una tale funzione definita in tale posizione. Quindi controlla l'ambito della classe base, scopre la funzione e di conseguenza associa la chiamata di funzione a questa particolare definizione.

Tuttavia, quando si ridefinisce la funzione con una firma diversa, il compilatore corrisponde alla chiamata con questa funzione, rileva incoerenza e si lamenta semplicemente.

è possibile modificare questo comportamento aggiungendo "utilizzando Base :: gogo;" nella defenisione di classe derivata. Spero che questo spieghi.

2

Si desidera sovrascrivere la funzione sovraccaricata ma le regole di occultamento non funzionano in questo modo.

"La regola nascosta dice che un'entità in un ambito interno nasconde le cose con lo stesso nome in un ambito esterno."

Nota, è irrilevante che ha firma diversa cioè gogo(int* a) nasconde tutti gogo(whatever) funzioni dalla base. L'override si verifica solo quando le tue funzioni hanno lo stesso nome, stesse firme e virtuale (quindi, solo gogo(int* a) verrà annullato).

Il libro di domande frequenti su C++ suggerisce di utilizzare "overload non virtuali che chiamano virtuals non sovraccaricati". (capitolo 29.05). In sostanza si crea non virtuale funzioni sovraccaricate in classe di base:

gogo(int a) and gogo(int* a)

che chiamerà le funzioni virtuali, rispettivamente:

gogo_i virtuale (int a) e gogo_pi virtuale (int * a)

E sovrascrive questo virtuale nella classe derivata.

Problemi correlati