2012-04-02 7 views
10

Sì, so che il downcast che utilizza dynamic_cast non può essere compilato se lo Base non è polimorfico, ma il mio problema non riguarda questo.`dynamic_cast` da Base a Derived

class Base { 
    public: 
     virtual void bar() 
     { 
      cout << "bar\n"; 
     } 
}; 

class Derived: public Base { 
    public: 
     void foo() 
     { 
      cout << "foo\n"; 
     } 
}; 

int main() 
{ 
    Base *pb; 
    Derived *pd; 

    pb = new Derived; //Base* points to a Derived object 
    pd = dynamic_cast<Derived*>(pb); 
    pd->foo(); //outputs foo 

    pb = new Base; //Base* points to a Base object 
    pd = dynamic_cast<Derived*>(pb); 
    pd->foo(); //outputs foo, too. Why? 
} 

ho pensato quando pb = new Derived;, pb in realtà punta a un oggetto Derived sta nel mucchio. Dopo pd = dynamic_cast<Derived*>(pb);, pd punta anche a tale oggetto Derived, quindi pd->foo() dovrebbe essere OK.

Ma quando pb = new Base;, cosa pb punti per è un oggetto Base in mucchio, poi dopo pd = dynamic_cast<Derived*>(pb);, come potrebbe pd->foo() funziona? dynamic_cast ha trasformato l'oggetto Base in heap in un oggetto Derived?

risposta

17

In C++, ogni istanza di una classe ha una propria versione di tipi di dati, ma tutte le classi condividono la stessa funzione in memoria (oltre che per le funzioni in linea). Nel tuo caso, quando si dice qualcosa come:

pd->foo(); 

si sono essenzialmente chiamando Derived::foo, che è una funzione in memoria e il compilatore sa dove si trova. Il fatto è che non dipende affatto da pd. Tuttavia, se si ha qualcosa di simile:

class Derived : public Base { 
    private: 
     int a; 

    public: 
     Derived() { a = 100; } 

     void foo() { 
      std::cout<<a<<std::endl; 
     } 
}; 

Poi, pd->foo() causerà un errore di segmentazione. Qui, il cast dinamico è fallito e quando viene chiamato il numero Derived::foo, viene passato come 0 come oggetto this. Nel caso precedente, nel caso precedente, non era mai stato utilizzato l'oggetto this. Tuttavia, nel secondo caso, viene utilizzato e quindi causa un errore di segmentazione.

+0

Quando dico 'pd-> foo();', allora 'foo()' viene chiamato non importa 'pd' è' NULL' o no? – Alcott

+0

@Alcott yes, ma il parametro 'this' verrà passato come NULL (poiché questo è il valore di' pd'). Da qui l'incidente previsto quando lo stai dereferendo accedendo a 'a' nell'esempio di Rohan. – littleadv

+1

Dipende in gran parte dal compilatore e, come ha detto @Luchian Grigore, può succedere di tutto. Quindi, nella maggior parte dei casi, sì, ma è qualcosa su cui non puoi contare. –

7

Nel tuo foo non si accede a this, che in questo caso deve essere NULL. Questo è ciò che restituisce dynamic_cast quando non è possibile eseguire il cast.

Fondamentalmente ci si trova nell'area "comportamento indefinito" qui.

+0

FYI, ho fatto +1 sulla tua risposta perché è corretta. * Ma * ancora non vedo come qualcosa di simile sia fortunato. È possibile imbattersi in cattivi bug difficili da tracciare perché il programma non si è arrestato in modo anomalo quando avrebbe dovuto. –

+1

@LuchianGrigore [Luck] (http://en.wiktionary.org/wiki/luck#Noun) (n) 1. Qualcosa che capita a qualcuno per caso, un evento casuale. – Potatoswatter

6

Si sta verificando un comportamento indefinito. È necessario verificare il tipo di reso di dynamic_cast.

pd = dynamic_cast<Derived*>(pb); 

Questo restituisce un valore nullo, e si chiama una funzione su un puntatore NULL. Tutto può succedere.

+0

Ma se 'pd == NULL', allora perché nessun errore di runtime? – Alcott

+0

@Alcott è un comportamento non definito. Tutto può succedere. –

+1

@Alcott tecnicamente la risposta di Luchian è corretta. Ma praticamente la ragione è perché è più semplice per gli sviluppatori del compilatore ignorarlo, dato che sono consentiti (perché lo standard non definisce cosa fare in questo caso), invece di implementare controlli del tempo di esecuzione complicati e costanti sul costo delle prestazioni ogni accesso puntatore. Quindi qualunque cosa accada - accade, e tutto è corretto. – littleadv

1

Si prega di preferire sempre di controllare se il cast ha successo prima di provare a usarlo. Immagino sia il vantaggio di usare la fusione. Puoi verificare se ha avuto successo o meno.

pd = dynamic_cast<Derived*>(pb); 
if(pd!=NULL) 
    pd->foo(); 

Se il cast non riesce pd ha valore NULL. Non utilizzare pd a meno che non si sia sicuri che abbia un valore. e quindi de riferimento solo a