Ho sentito che la static_cast
funzione dovrebbe essere preferita al casting in stile C o semplice. È vero? Perché?
Ho sentito che la static_cast
funzione dovrebbe essere preferita al casting in stile C o semplice. È vero? Perché?
Risposte:
La ragione principale è che classici calchi C non fanno distinzione tra ciò che noi chiamiamo static_cast<>()
, reinterpret_cast<>()
, const_cast<>()
, e dynamic_cast<>()
. Queste quattro cose sono completamente diverse.
A static_cast<>()
è di solito sicuro. Esiste una conversione valida nella lingua o un costruttore appropriato che lo rende possibile. L'unica volta che è un po 'rischioso è quando ti abbassi a una classe ereditata; devi assicurarti che l'oggetto sia effettivamente il discendente che asserisci di essere, in modo esterno al linguaggio (come una bandiera nell'oggetto). A dynamic_cast<>()
è sicuro fintanto che il risultato è selezionato (puntatore) o viene presa in considerazione una possibile eccezione (riferimento).
Una reinterpret_cast<>()
(o una const_cast<>()
) d'altra parte è sempre pericolosa. Dici al compilatore: "fidati di me: so che questo non sembra un foo
(sembra che non sia mutabile), ma lo è".
Il primo problema è che è quasi impossibile dire quale accadrà in un cast in stile C senza guardare pezzi di codice grandi e dispersi e conoscere tutte le regole.
Supponiamo che:
class CDerivedClass : public CMyBase {...};
class CMyOtherStuff {...} ;
CMyBase *pSomething; // filled somewhere
Ora, questi due sono compilati allo stesso modo:
CDerivedClass *pMyObject;
pMyObject = static_cast<CDerivedClass*>(pSomething); // Safe; as long as we checked
pMyObject = (CDerivedClass*)(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<CMyOtherStuff*>(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 di tipo C sono troppo difficili da individuare. In espressioni complesse può essere molto difficile vedere i cast in stile C. È praticamente impossibile scrivere uno strumento automatizzato che deve individuare i cast in stile C (ad esempio uno strumento di ricerca) senza un front-end del compilatore C ++ completo. D'altra parte, è facile cercare "static_cast <" o "reinterpret_cast <".
pOther = reinterpret_cast<CMyOtherStuff*>(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.
static_cast
per abbattere una gerarchia ereditaria, ma piuttosto dynamic_cast
. Ciò restituirà il puntatore null o un puntatore valido.
static_cast
in quella situazione. dynamic_cast
potrebbe essere più sicuro, ma non è sempre l'opzione migliore. A volte sai che un puntatore punta a un dato sottotipo, per mezzo opaco al compilatore, e a static_cast
è più veloce. In almeno alcuni ambienti, dynamic_cast
richiede il supporto del compilatore opzionale e i costi di runtime (abilitazione di RTTI) e potresti non volerlo abilitare solo per un paio di controlli che puoi fare tu stesso. RTTI di C ++ è solo una possibile soluzione al problema.
static_cast
. L'equivalente C di reinterpret_cast
è *(destination_type *)&
, cioè prendere l'indirizzo dell'oggetto, lanciare quell'indirizzo su un puntatore a un tipo diverso e quindi dereferenziare. Tranne nel caso di tipi di carattere o di alcuni tipi di struttura per i quali C definisce il comportamento di questo costrutto, generalmente si ottiene un comportamento indefinito in C.
int
(e int
solo), perché usare static_cast<int>
vs. (int)
come unico vantaggio sembra essere con variabili di classe e puntatori. Richiedi di approfondire questo.
int
dynamic_cast
non si applica, ma tutti gli altri motivi sono validi . Ad esempio: supponiamo che v
sia un parametro di funzione dichiarato come float
, quindi lo (int)v
è static_cast<int>(v)
. Ma se si modifica il parametro in float*
, (int)v
diventa tranquillamente reinterpret_cast<int>(v)
mentre static_cast<int>(v)
è illegale e correttamente catturato dal compilatore.
Un consiglio pragmatico: puoi cercare facilmente la parola chiave static_cast nel tuo codice sorgente se prevedi di riordinare il progetto.
int
parametro.
In breve :
static_cast<>()
ti dà la possibilità di compilare il tempo di verifica, il cast di C-Style no.static_cast<>()
può essere individuato facilmente ovunque all'interno di un codice sorgente C ++; al contrario, il cast di C_Style è più difficile da individuare.- Le intenzioni vengono trasmesse molto meglio usando i cast C ++.
Più spiegazione :
Il cast statico esegue conversioni tra tipi compatibili . È simile al cast in stile C, ma è più restrittivo. Ad esempio, il cast in stile C consentirebbe 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 provocherà un errore di runtime o sovrascriverà della memoria adiacente.
*p = 5; // run-time error: stack corruption
Contrariamente al cast in stile C, il cast statico consentirà al compilatore di verificare la compatibilità dei tipi di dati puntatore e puntatore, il che consente al programmatore di rilevare questa assegnazione errata del puntatore durante la compilazione.
int *q = static_cast<int*>(&c); // compile-time error
Maggiori informazioni su:
Qual è la differenza tra static_cast <> e il cast in stile C
e il
cast regolare rispetto a static_cast vs. dynamic_cast
static_cast<>()
sia più leggibile. Voglio dire, a volte lo è, ma la maggior parte delle volte - specialmente sui tipi interi di base - è solo orribilmente e inutilmente prolisso. Ad esempio: questa è una funzione che scambia i byte di una parola a 32 bit. Sarebbe quasi impossibile leggere usando i static_cast<uint##>()
cast, ma è abbastanza facile da capire usando i (uint##)
cast. Immagine del codice: imgur.com/NoHbGve
always
neanche. (ma la maggior parte delle volte sì) Ci sono sicuramente casi in cui il cast in stile c è molto più leggibile. Questo è uno dei motivi per cui il casting in stile c è ancora attivo e attivo in c ++ imho. :) A proposito, è stato un bell'esempio
(uint32_t)(uint8_t)
) per ottenere che i byte oltre al minimo vengano resettati. Per questo c'è bitwise e ( 0xFF &
). L'uso dei calchi sta offuscando l'intenzione.
La domanda è più grande del semplice utilizzo di wither static_cast o casting in stile C perché ci sono diverse cose che accadono quando si usano i cast in stile C. Gli operatori di casting C ++ intendono rendere più esplicite queste operazioni.
In superficie, i cast di static_cast e C style sembrano la stessa cosa, ad esempio quando si lancia un valore su un altro:
int i;
double d = (double)i; //C-style cast
double d2 = static_cast<double>( i ); //C++ cast
Entrambi eseguono il cast del valore intero in un doppio. Tuttavia, quando si lavora con i puntatori, le cose si complicano. 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<char*>( new int( 5 ) ); //(3) compile time error
In questo esempio (1) forse OK perché l'oggetto indicato da A è in realtà un'istanza di B. Ma cosa succede se a quel punto nel codice non si sa a cosa effettivamente indica? (2) forse perfettamente legale (vuoi solo guardare un byte dell'intero), ma potrebbe anche essere un errore nel qual caso un errore sarebbe carino, come (3). Gli operatori di casting C ++ intendono esporre questi problemi nel codice fornendo errori di compilazione o di runtime quando possibile.
Quindi, per un rigoroso "casting di valore" puoi usare static_cast. Se si desidera eseguire il casting polimorfico di runtime dei puntatori, utilizzare dynamic_cast. Se vuoi davvero dimenticare i tipi, puoi usare reintrepret_cast. E per lanciare const dalla finestra c'è const_cast.
Rendono solo 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
, il che è una buona cosa.
Vedi Introduzione al C ++ efficace
Si tratta di quanta sicurezza del tipo vuoi imporre.
Quando scrivi (bar) foo
(il che equivale a reinterpret_cast<bar> foo
se non hai fornito un operatore di conversione del tipo) stai dicendo al compilatore di ignorare la sicurezza del tipo, e fai solo come è stato detto.
Quando scrivi static_cast<bar> foo
stai chiedendo al compilatore di controllare almeno che la conversione del tipo abbia senso e, per i tipi integrali, di inserire un codice di conversione.
MODIFICA 26/02/2014
Ho scritto questa risposta più di 5 anni fa e ho sbagliato. (Vedi commenti.) Ma ottiene ancora voti!
static_cast<bar>(foo)
, tra parentesi. Lo stesso per reinterpret_cast<bar>(foo)
.
I cast di stile C sono facili da perdere in un blocco di codice. I cast in stile C ++ non sono solo buone pratiche; offrono un grado di flessibilità molto maggiore.
reinterpret_cast consente integrali conversioni di tipo puntatore, tuttavia può essere pericoloso se utilizzato in modo improprio.
static_cast offre una buona conversione per tipi numerici, ad es. da enum a ints o ints a float o qualsiasi tipo di dato di cui si è certi del tipo. Non esegue alcun controllo 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 comporta 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 esplicitamente definite nelle classi, nonché per eseguire conversioni standard tra tipi fondamentali:
double d = 3.14159265;
int i = static_cast<int>(d);
static_cast<int>(d)
, tuttavia, quando (int)d
è molto più conciso e leggibile? (Intendo nel caso dei tipi base, non dei puntatori oggetto.)
(int)d
quando int{d}
è molto più leggibile? Funzione di costruzione o simil-funzione se presente ()
, la sintassi non è così veloce da trasformarsi in un labirinto da incubo di parentesi in espressioni complesse. In questo caso, sarebbe int i{d}
invece di int i = (int)d
. IMO molto migliore. Detto questo, quando ho solo bisogno di un temporaneo in un'espressione, uso static_cast
e non ho mai usato i cast del costruttore, non credo. Uso solo (C)casts
quando scrivo in fretta il debug cout
s ...