2010-09-19 18 views

risposta

80

Per comprendere il sistema di trasmissione è necessario immergersi nel modello dell'oggetto.

La rappresentazione classica di un semplice modello di gerarchia è di contenimento: che se B deriva dal A allora l'oggetto B sarà infatti contenere un A sotto-oggetto a fianco i propri attributi.

Con questo modello, il downcasting è una semplice manipolazione puntatore, con un offset noto al momento della compilazione che dipende dal layout di memoria di B.

Questo è quello static_cast do: un getto statico è doppiato statico perché il calcolo di quanto necessario per il getto viene effettuata al momento della compilazione, sia esso puntatori o conversioni (*).

Tuttavia, quando l'ereditarietà di virtual inizia a diventare un po 'più difficile. Il problema principale è che con l'ereditarietà virtual tutte le sottoclassi condividono la stessa istanza del subobject. Per fare ciò, B avrà un puntatore a A, anziché un A corretto e l'oggetto classe base A verrà istanziato all'esterno di B.

Pertanto, al momento della compilazione è impossibile dedurre l'aritmetica del puntatore necessaria: dipende dal tipo di runtime dell'oggetto.

Ogni volta che c'è un tipo di runtime dipendenza, è necessario RTTI (Runtime Informazioni Tipo), e facendo uso di RTTI per calchi è il lavoro di dynamic_cast.

In sintesi:

  • fase di compilazione bassi: static_cast
  • runtime bassi: dynamic_cast

Gli altri due sono anche compilare in tempo getta, ma sono così specifico che è facile ricordare a cosa servono ... e sono maleodoranti, quindi meglio non usarli comunque.

(*) Come indicato da @curiousguy nei commenti, ciò vale solo per downcasting. A static_cast consente l'upcasting indipendentemente dall'ereditarietà virtuale o semplice, anche se quindi il cast non è necessario.

+4

Bella risposta che mi ha fatto capire come funziona l'ereditarietà virtuale!+1 – undu

+0

Mi piace la tua risposta, ma l'OP stava apparentemente chiedendo un errore per DOWNCASTING piuttosto che upcasting. – h9uest

+0

@h9uest: Grazie per aver segnalato il problema, ho cambiato "up-casting" in "downcasting" e ora tutto è a posto. –

11

Per quanto ne so, è necessario utilizzare dynamic_cast perché l'eredità è virtual e si sta downcasting.

6

Non è possibile utilizzare static_cast in questa situazione perché il compilatore non conosce l'offset di B relativo a A in fase di compilazione. L'offset deve essere calcolato in fase di esecuzione in base al tipo esatto dell'oggetto più derivato. Pertanto è necessario utilizzare dynamic_cast.

+0

Quando si converte un derivato in una base virtuale, è possibile utilizzare 'static_cast'. – curiousguy

+0

@curiousguy: sì, ma la domanda riguarda la conversione della base in derivata. – ybungalobill

+0

Non è chiaro il motivo per cui il tuo argomento non si applica anche a derivati ​​su base. – curiousguy

4

Sì, devi usare un dynamic_cast, ma dovrai rendere la classe base A polimorfica, ad es. aggiungendo un virtual dtor.

+1

o aggiungendo almeno un metodo virtuale. – Liton

4

Secondo documenti standard,

Sezione 5.2.9 - 9, per Fusioni Statico,

An rvalue of type “pointer to cv1 B,” where B is a class type, can be converted to an rvalue of type “pointer to cv2 D,” where D is a class derived (clause 10) from B, if a valid standard conversion from “pointer to D” to “pointer to B” exists (4.10), cv2 is the same cv-qualification as, or greater cv-qualification than, cv1, and B is neither a virtual base class of D nor a base class of a virtual base class of D.

Quindi, non è possibile e si dovrebbe utilizzare dynamic_cast ...

1

$5.2.9/2- "An expression e can be explicitly converted to a type T using a static_cast of the form static_cast(e) if the declaration “T t(e);” is well-formed, for some invented temporary variable t (8.5)."

Nel codice che si sta tentando static_cast con 'T = B *' e 'E = A *'

ora 'B * t (A *)' non è ben formato in C++ (ma 'A * t (B *)' è perché 'A' è una base virtuale non ambigua e accessibile di 'B.' Quindi il codice dà errore

+0

Citazione errata. – curiousguy

1

Non so se questo è "sicuro" ma .

Supponendo

B derivata dal ceppo A (e un virtuale pura)

Poiché so che un puntatore a B ancora rimane un puntatore a B.

class A 
    { 
      virtual void doSomething(const void* p) const =0; 
    }; 

    class B 
    { 
    public: 
      int value; 
      virtual void doSomething(const void*p)const 
      { 
      const B * other = reinterpret_cast<const B*>(p); 
      cout<<"hello!"<< other->value <<endl; 
      } 
    }; 

    int main() 
    { 
      B foo(1),bar(2); 
      A * p = &foo, q=&bar; 
      p->doSomething(q); 
      return 0; 
    } 

questo programma esegue correttamente e restituire la stampa "ciao!" e il valore dell'altro oggetto (in questo caso "2").

a proposito, quello che sto facendo è molto pericoloso (personalmente do un ID diverso a ogni classe e asserisco dopo aver reinterpretato il casting che l'ID corrente è uguale ad altri ID per essere sicuro che stiamo facendo qualcosa con 2 uguali classi) e come vedete mi sono limitato ai metodi "const". Quindi questo funzionerà con i metodi "non-const", ma se fai qualcosa di sbagliato, catturare il bug sarà quasi impossibile. E anche con l'affermazione c'è una possibilità su 4 miliardi di affermare l'asserzione anche quando si suppone che fallisca (asserire (ID == altro-> ID);)

A proposito .. Un buon design OO dovrebbe Non ho bisogno di questo genere di cose, ma nel mio caso ho cercato di rifattorizzare/ridisegnare il codice senza essere in grado di abbandonare l'uso di reinterpretare il casting. in generale, puoi evitare questo tipo di cose.

+0

Sei sicuro che sia necessario? –

+0

specifico per il tuo problema. un re-design dovrebbe prevenirlo nella maggior parte dei casi (evita il mio esempio se puoi). oh caro dimentico un "const". – GameDeveloper

+0

Voglio dire, sei sicuro di averne bisogno? Cosa succede se do DoSomething un puntatore int? Ciò fallirebbe in modo disastroso. Perché non utilizzare un cast dinamico e controllare il risultato? Non so quali sono i tuoi requisiti esatti, ma credo che se introduci il sistema del polimorfismo statico (ad esempio il modello CRTP) puoi inventare qualcosa di più sicuro. Il polimorfismo statico –

Problemi correlati