Perché usare static_cast (x) invece di (int) x?

Ho sentito dire che la funzione static_cast dovrebbe essere preferibile allo stile C o alla semplice fusione di funzioni. È vero? Perché?

Il motivo principale è che i cast classici di C non fanno distinzione tra ciò che chiamiamo static_cast<>() , reinterpret_cast<>() , const_cast<>() e dynamic_cast<>() . Queste quattro cose sono completamente diverse.

Un static_cast<>() solito è sicuro. C’è una conversione valida nella lingua o un costruttore appropriato che lo rende ansible. L’unica volta in cui è un po ‘rischioso è quando si scende a una class ereditata; devi assicurarti che l’object sia effettivamente il discendente che tu affermi che sia, per mezzo esterno alla lingua (come una bandiera nell’object). Un dynamic_cast<>() è sicuro fino a quando il risultato è selezionato (puntatore) o viene presa in considerazione una ansible eccezione (riferimento).

Un reinterpret_cast<>() (o un const_cast<>() ) d’altra parte è sempre pericoloso. Dite al compilatore: “fidatevi di me: so che questo non sembra un foo (sembra che non sia mutabile), ma lo è”.

Il primo problema è che è quasi imansible stabilire quale sarà il cast in stile C senza guardare pezzi di codice ampi e dispersi e conoscere tutte le regole.

Assumiamo questi:

 class CMyClass : public CMyBase {...}; class CMyOtherStuff {...} ; CMyBase *pSomething; // filled somewhere 

Ora, questi due sono compilati allo stesso modo:

 CMyClass *pMyObject; pMyObject = static_cast(pSomething); // Safe; as long as we checked pMyObject = (CMyClass*)(pSomething); // Same as static_cast<> // Safe; as long as we checked // but harder to read 

Tuttavia, vediamo questo codice quasi identico:

 CMyOtherStuff *pOther; pOther = static_cast(pSomething); // Compiler error: Can't convert pOther = (CMyOtherStuff*)(pSomething); // No compiler error. // Same as reinterpret_cast<> // and it's wrong!!! 

Come puoi vedere, non esiste un modo semplice per distinguere tra le due situazioni senza sapere molto su tutte le classi coinvolte.

Il secondo problema è che i cast in stile C sono troppo difficili da localizzare. Nelle espressioni complesse può essere molto difficile vedere i cast in stile C. È praticamente imansible scrivere uno strumento automatico che abbia bisogno di localizzare i cast in stile C (ad esempio uno strumento di ricerca) senza un front-end compilatore C ++ completo. D’altra parte, è facile cercare “static_cast <" o "reinterpret_cast <".

 pOther = reinterpret_cast(pSomething); // No compiler error. // but the presence of a reinterpret_cast<> is // like a Siren with Red Flashing Lights in your code. // The mere typing of it should cause you to feel VERY uncomfortable. 

Ciò significa che non solo i cast in stile C sono più pericolosi, ma è molto più difficile trovarli tutti per assicurarsi che siano corretti.

Un consiglio pragmatico: puoi cercare facilmente la parola chiave static_cast nel codice sorgente se hai intenzione di riordinare il progetto.

In breve :

  1. static_cast<>() ti dà un’abilità di controllo del tempo di compilazione, il cast di C-Style no.
  2. static_cast<>() può essere facilmente individuato ovunque all’interno di un codice sorgente C ++; al contrario, il cast di C_Style è più difficile da individuare.
  3. Le intenzioni sono trasmesse molto meglio usando i cast di C ++.

Ulteriori spiegazioni :

Il cast statico esegue conversioni tra tipi compatibili . È simile al cast in stile C, ma è più restrittivo. Ad esempio, il cast in stile C consente a un puntatore intero di puntare a un carattere.

 char c = 10; // 1 byte int *p = (int*)&c; // 4 bytes 

Poiché ciò comporta un puntatore a 4 byte che punta a 1 byte di memoria allocata, la scrittura su questo puntatore causerà un errore di runtime o sovrascriverà la memoria adiacente.

 *p = 5; // run-time error: stack corruption 

In contrasto con il cast in stile C, il cast statico consentirà al compilatore di verificare che i tipi di dati di puntatore e punta siano compatibili, il che consente al programmatore di cogliere questa errata assegnazione del puntatore durante la compilazione.

 int *q = static_cast(&c); // compile-time error 

Leggi di più su:
Qual è la differenza tra static_cast <> e casting in stile C.
e
Cast normale vs static_cast vs dynamic_cast

La domanda è più grande del semplice uso di wither static_cast o casting in stile C perché ci sono cose diverse che accadono quando si usano i cast di stile C. Gli operatori di casting C ++ hanno lo scopo di rendere più esplicite queste operazioni.

Sulla superficie i cast di static_cast e di stile C sembrano la stessa cosa, ad esempio quando si trasmette un valore a un altro:

 int i; double d = (double)i; //C-style cast double d2 = static_cast( i ); //C++ cast 

Entrambi di questi cast il valore intero a un doppio. Tuttavia, quando si lavora con i puntatori, le cose diventano più complicate. qualche esempio:

 class A {}; class B : public A {}; A* a = new B; B* b = (B*)a; //(1) what is this supposed to do? char* c = (char*)new int( 5 ); //(2) that weird? char* c1 = static_cast( new int( 5 ) ); //(3) compile time error 

In questo esempio (1) forse OK perché l’object puntato da A è in realtà un’istanza di B. Ma cosa succede se non si conosce a quel punto nel codice ciò che effettivamente indica? (2) forse perfettamente legale (si vuole solo vedere un byte del numero intero), ma potrebbe anche essere un errore nel qual caso un errore sarebbe bello, come (3). Gli operatori di casting C ++ hanno lo scopo di esporre questi problemi nel codice fornendo, se ansible, errori in fase di compilazione o in fase di esecuzione.

Quindi, per il rigoroso “lancio di valore” puoi usare static_cast. Se si desidera che i puntatori polimorfici di runtime dei puntatori utilizzino dynamic_cast. Se vuoi davvero dimenticare i tipi, puoi usare reintrepret_cast. E per lanciare const fuori dalla finestra c’è const_cast.

Rendono il codice più esplicito in modo che sembri che tu sappia cosa stavi facendo.

static_cast significa che non puoi accidentalmente const_cast o reinterpret_cast , che è una buona cosa.

  1. Permette ai cast di essere trovati facilmente nel tuo codice usando grep o strumenti simili.
  2. Rende esplicito il tipo di cast che stai facendo e coinvolgendo l’aiuto del compilatore nell’implementarlo. Se vuoi solo escludere le costanti, puoi utilizzare const_cast, che non ti permetterà di fare altri tipi di conversioni.
  3. I cast sono intrinsecamente brutti – tu come programmatore stai ignorando il modo in cui il compilatore tratterà normalmente il tuo codice. Stai dicendo al compilatore: “Conosco meglio di te”. Stando così le cose, ha senso che l’esecuzione di un cast dovrebbe essere una cosa abbastanza dolorosa da fare, e che dovrebbero rimanere nel tuo codice, poiché sono una probabile fonte di problemi.

Vedi Introduzione C ++ efficace

Riguarda quanto tipo di sicurezza vuoi imporre.

Quando scrivi (bar) foo (che equivale a reinterpret_cast foo se non hai fornito un operatore di conversione di tipo) stai dicendo al compilatore di ignorare la sicurezza del tipo, e fai come ti viene detto.

Quando scrivi static_cast foo chiedi al compilatore di controllare almeno che la conversione del tipo abbia senso e, per i tipi interi, di inserire un codice di conversione.


EDIT 2014-02-26

Ho scritto questa risposta più di 5 anni fa e ho sbagliato. (Vedi i commenti.) Ma ottiene ancora voti positivi!

I cast di stile C sono facili da perdere in un blocco di codice. I cast di stile C ++ non sono solo migliori pratiche; offrono un grado molto maggiore di flessibilità.

reinterpret_cast consente l’integrale delle conversioni del tipo di puntatore, tuttavia può essere pericoloso se usato in modo improprio.

static_cast offre una buona conversione per i tipi numerici, ad es. da enumerazioni a int e inti a float o qualsiasi tipo di dati di cui si è certi del tipo. Non esegue alcuna verifica del tempo di esecuzione.

dynamic_cast d’altra parte eseguirà questi controlli segnalando eventuali assegnazioni o conversioni ambigue. Funziona solo su puntatori e riferimenti e incorre in un sovraccarico.

Ce ne sono un paio, ma questi sono i principali che incontrerai.

static_cast, oltre a manipolare i puntatori alle classi, può anche essere utilizzato per eseguire conversioni definite esplicitamente nelle classi, nonché per eseguire conversioni standard tra tipi fondamentali:

 double d = 3.14159265; int i = static_cast(d);