Domanda

Ho sentito che la funzione static_cast dovrebbe essere preferita al casting in stile C o semplice. È vero? Perché?

È stato utile?

Soluzione

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

Un static_cast < > () è generalmente 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). Un dynamic_cast < > () è sicuro fintanto che il risultato è controllato (puntatore) o viene presa in considerazione una possibile eccezione (riferimento).

Un reinterpret_cast < > () (o un const_cast < > () ) d'altra parte è sempre pericoloso. Dici al compilatore: " fidati di me: so che questo non sembra un foo (sembra che non sia mutabile), ma è " ;.

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 in stile C sono troppo difficili da localizzare. 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.

Altri suggerimenti

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

  

In breve :

     
      
  1. static_cast < > () ti offre una capacità di controllo del tempo di compilazione, stile C   cast no.
  2.   
  3. 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.
  4.   
  5. Le intenzioni vengono trasmesse molto meglio usando i cast C ++.
  6.   
     

Altre spiegazioni :

     

Il cast statico esegue conversioni tra tipi compatibili . esso   è simile al cast in stile C, ma è più restrittivo. Per 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ò si traduce in un puntatore a 4 byte che punta a 1 byte di allocato   memoria, la scrittura su questo puntatore causerà un errore di runtime o   sovrascriverà un po 'di memoria adiacente.

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

Contrariamente al cast in stile C, il cast statico consentirà il   compilatore per verificare che siano i tipi di dati puntatore e punta   compatibile, che consente al programmatore di rilevare ciò in modo errato   assegnazione del puntatore durante la compilazione.

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

Ulteriori informazioni su:
Qual è la differenza tra static_cast < > e casting in stile C
e
Cast regolare vs. static_cast vs. dynamic_cast

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 lanciano il valore intero su un doppio. Tuttavia, quando si lavora con i puntatori, le cose si complicano. alcuni esempi:

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 è davvero 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 "value casting" " 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 più esplicito il codice in modo che sembri che tu sappia cosa stavi facendo.

static_cast significa che non puoi accidentalmente const_cast o reinterpret_cast , il che è positivo.

  1. Permette di trovare facilmente i cast in il tuo codice usando grep o simili utensili.
  2. Rende esplicito che tipo     di cast che stai facendo e coinvolgente     l'aiuto del compilatore per applicarlo.     Se vuoi solo buttare via     costanza, quindi puoi usare     const_cast, che non ti permetterà     per fare altri tipi di conversioni.
  3. I cast sono intrinsecamente brutti - tu come         un programmatore sta annullando come         il compilatore normalmente tratta il tuo         codice. Stai dicendo al         compilatore, "lo so meglio di te."         Stando così le cose, ha senso         che eseguire un cast dovrebbe essere a         cosa moderatamente dolorosa da fare, e         che dovrebbero sporgere nel tuo         codice, poiché sono una fonte probabile         di problemi.

Vedi C ++ efficace Introduzione

Si tratta di quanta sicurezza del tipo vuoi imporre.

Quando scrivi (bar) foo (che equivale a reinterpret_cast < bar > foo se non hai fornito un operatore di conversione del tipo) stai dicendo al compilatore per ignorare la sicurezza dei tipi e fare solo come viene 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, 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);

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 nelle conversioni di tipo puntatore, tuttavia può non essere sicuro se utilizzato in modo improprio.

static_cast offre una buona conversione per tipi numerici, ad es. da come 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.

D'altro canto,

dynamic_cast 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.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top