2010-10-11 13 views
9
#include <iostream> 
using namespace std; 
class X{ 
    public: 
    virtual void f(){} 
}; 

class Y { 
    public: 
    virtual void g() {} 
}; 

int main() 
{ 
    X * x = new X(); 
    Y* y = dynamic_cast<Y*>(x); //A 
    // Y* y = static_cast<Y*>(x); //B 
    cout << y << endl; 
} 

A compilazione mentre B non. Comprendo perché il codice B non viene compilato, ma perché lo A viene compilato anche se X e Y sono tipi completamente non correlati?Cast dinamico che funziona su tipi non correlati

risposta

1

Il compilatore non si preoccupa, perché è dynamic_cast. sarà NULL dopo il cast.

5

Il cast dinamico utilizza le informazioni sul tipo di runtime. Quindi questo è legale in questo caso ma restituirà un puntatore nullo. Il cast statico viene valutato dal compilatore.

1

dynamic_cast esegue il controllo del tipo in fase di esecuzione, utilizzando RTTI, mentre static_cast lo esegue in fase di compilazione. Quindi, quello che otterresti se avessi a disposizione lo A sarebbe y == NULL.

Ti suggerisco read up su cast di tipo C++.

1

Se [utilizzando dinamico cast] si tenta di gettare a un tipo di puntatore, e quel tipo non è un tipo effettivo dell'oggetto argomento, quindi il risultato del cast sarà NULL.

Y* y = dynamic_cast<Y*>(x); // NULL because `X` is not a `Y` 
20

Questo è il motivo per cui dynamic_cast è consentito tra i tipi non correlati:

class X{ 
    public: 
    virtual void f(){} 
}; 

class Y { 
    public: 
    virtual void g() {} 
}; 

class Z : public X, public Y {}; 

int main() 
{ 
    X* x = new Z(); 
    Y* y = dynamic_cast<Y*>(x); // compiles and yields non-null pointer 
} 
1

C'è una differenza enorme tra static_cast e dynamic_cast, mi limiterò a riduco la discussione al mondo degli oggetti.

A static_cast può essere utilizzato per il (infame) up-cast. Cioè:

void foo(Base& b) { Derived& d = static_cast<Derived&>(b); } 

Il compilatore può valutare se questo è legale o no, perché avere la definizione di Derived si sa o meno Derived è in realtà un discendente di Base.

per le gerarchie non banali:

struct Base {}; 
struct D1: Base {}; 
struct D2: Base {}; 
struct Derived: D1, D2 {}; 

Ciò produrrebbe un errore: il compilatore non saprebbe da quale delle basi (quella da D1 o quello da D2 siete venuti).

O se si è utilizzato virtual eredità:

struct VDerived: virtual VBase {}; 

il compilatore avrebbe bisogno di informazioni di runtime, e quindi la compilazione fallirebbe anche.

A dynamic_cast tuttavia è molto più intelligente, anche se ciò comporta un sovraccarico di runtime. Il compilatore genera informazioni sugli oggetti generalmente conosciuto come RTTI (Runtime Type informazioni), che il dynamic_cast esplorerà per consentire:

Fusioni seconda del vero tipo runtime:

// will throw `bad_cast` if b is not a `Derived` 
void foo(Base& b) { Derived& d = dynamic_cast<Derived&>(b); } 

Fusioni attraversato rami:

struct X {}; 
struct Y {}; 

void foo(X* x) { Y* y = dynamic_cast<Y*>(x); } 

// If I add: 
struct Z: X, Y {}; 

// then if x is in fact a Z, y will be non-null 

Trasmetti quando è coinvolta l'ereditarietà virtuale.

void bar(VBase* vb) { VDerived* vd = dynamic_cast<VDerived*>(vb); } 

Fusioni per void* per ottenere il vero indirizzo dell'oggetto (questo è un po 'speciale).

void foobar(X* x) 
{ 
    void* xAddr = dynamic_cast<void*>(x); 

    Y* y = dynamic_cast<Y*>(y); 
    void* yAddr = dynamic_cast<void*>(y); 

    Z* z = dynamic_cast<Z*>(x); 
    void* zAddr = dynamic_cast<void*>(z); 
    if (z) { assert(xAddr == yAddr && xAddr == zAddr); } 
} 

noti che questa non funziona se c'è un'ambiguità causa della gerarchia (come nell'esempio D1/D2 sopra).

Problemi correlati