Domanda Quando devono essere usati static_cast, dynamic_cast, const_cast e reinterpret_cast?


Quali sono gli usi corretti di:

  • static_cast
  • dynamic_cast
  • const_cast
  • reinterpret_cast
  • Cast in stile C. (type)value
  • Cast di stile funzionale type(value)

Come si decide quale utilizzare in quali casi specifici?


2047
2017-12-01 20:11


origine


risposte:


static_cast è il primo cast che dovresti provare a usare. Fa cose come conversioni implicite tra tipi (come int a floato puntatore a void*), e può anche chiamare funzioni di conversione esplicite (o implicite). In molti casi, affermando esplicitamente static_cast non è necessario, ma è importante notare che il T(something) la sintassi è equivalente a (T)something e dovrebbe essere evitato (ne parleremo più avanti). UN T(something, something_else) è sicuro, tuttavia, e garantito per chiamare il costruttore.

static_cast può anche eseguire il cast attraverso le gerarchie di ereditarietà. Non è necessario quando si lancia verso l'alto (verso una classe base), ma quando si lancia verso il basso può essere usato fino a quando non viene lanciato virtual eredità. Non esegue il controllo, tuttavia, ed è un comportamento indefinito static_cast giù una gerarchia ad un tipo che non è in realtà il tipo di oggetto.


const_cast può essere usato per rimuovere o aggiungere const a una variabile; nessun altro cast di C ++ è in grado di rimuoverlo (nemmeno reinterpret_cast). È importante notare che la modifica di un precedente const il valore è solo indefinito se la variabile originale è const; se lo usi per prendere il const da un riferimento a qualcosa che non è stato dichiarato con const, è sicuro. Questo può essere utile quando si sovraccaricano le funzioni membro in base a const, per esempio. Può anche essere usato per aggiungere const a un oggetto, tale da richiamare un sovraccarico della funzione membro.

const_cast funziona anche allo stesso modo volatile, anche se questo è meno comune.


dynamic_cast è utilizzato quasi esclusivamente per la manipolazione del polimorfismo. Puoi lanciare un puntatore o riferimento a qualsiasi tipo polimorfico a qualsiasi altro tipo di classe (un tipo polimorfico ha almeno una funzione virtuale, dichiarata o ereditata). Puoi usarlo per più di un semplice lancio verso il basso - puoi lanciare lateralmente o addirittura su un'altra catena. Il dynamic_cast cercherà l'oggetto desiderato e lo restituirà se possibile. Se non può, ritornerà nullptr nel caso di un puntatore, o lanciare std::bad_cast nel caso di un riferimento.

dynamic_cast ha alcune limitazioni, però. Non funziona se ci sono più oggetti dello stesso tipo nella gerarchia dell'ereditarietà (il cosiddetto 'diamante temuto') e non stai usando virtual eredità. Può anche passare attraverso l'eredità pubblica - non riuscirà mai a viaggiare protected o private eredità. Questo è raramente un problema, tuttavia, poiché tali forme di ereditarietà sono rare.


reinterpret_cast è il cast più pericoloso e dovrebbe essere usato con parsimonia. Trasforma un tipo direttamente in un altro - come il cast del valore da un puntatore a un altro o la memorizzazione di un puntatore in un into tutti i tipi di altre cose cattive. In gran parte, l'unica garanzia che ottieni reinterpret_cast è che normalmente se si restituisce il risultato al tipo originale, si otterrà lo stesso valore esatto (ma non se il tipo intermedio è inferiore al tipo originale). Ci sono un certo numero di conversioni reinterpret_cast non posso fare anche questo. Viene utilizzato principalmente per conversioni e manipolazioni bit particolarmente strane, come trasformare un flusso di dati non elaborati in dati reali o archiviare dati nei bit bassi di un puntatore allineato.


Cast in stile C. e cast di stile funzionale sono i cast che usano (type)object o type(object), rispettivamente. Un cast in stile C è definito come il primo dei seguenti che ha esito positivo:

  • const_cast
  • static_cast (anche se ignora le restrizioni di accesso)
  • static_cast (vedi sopra), quindi const_cast
  • reinterpret_cast
  • reinterpret_cast, poi const_cast

Può quindi essere usato come sostituto di altri calchi in alcuni casi, ma può essere estremamente pericoloso a causa della capacità di devolvere in un reinterpret_caste quest'ultimo dovrebbe essere preferito quando è necessaria la trasmissione esplicita, a meno che tu non sia sicuro static_cast riuscirà o reinterpret_cast avrà esito negativo. Anche allora, considera l'opzione più lunga e più esplicita.

I cast di tipo C ignorano anche il controllo degli accessi durante l'esecuzione di un static_cast, il che significa che hanno la capacità di eseguire un'operazione che nessun altro cast può eseguire. Questo è principalmente un kludge, però, e nella mia mente è solo un'altra ragione per evitare i cast in stile C.


2207
2017-12-01 20:22



Uso dynamic_cast per convertire puntatori / riferimenti all'interno di una gerarchia di ereditarietà.

Uso static_cast per le conversioni di tipo ordinario.

Uso reinterpret_cast per la reinterpretazione a basso livello dei pattern di bit. Usare con estrema cautela.

Uso const_cast per lanciare via const/volatile. Evita questo se non sei bloccato usando un'API const-errata.


283
2018-01-21 04:53



(Molte delle spiegazioni teoriche e concettuali sono state fornite in precedenza) 

Di seguito sono alcuni dei esempi pratici quando l'ho usato static_cast, dynamic_cast, const_cast, reinterpret_cast.

(Si riferisce anche a questo per capire la spiegazione: http://www.cplusplus.com/doc/tutorial/typecasting/)

static_cast:

OnEventData(void* pData)

{
  ......

  //  pData is a void* pData, 

  //  EventData is a structure e.g. 
  //  typedef struct _EventData {
  //  std::string id;
  //  std:: string remote_id;
  //  } EventData;

  // On Some Situation a void pointer *pData
  // has been static_casted as 
  // EventData* pointer 

  EventData *evtdata = static_cast<EventData*>(pData);
  .....
}

dynamic_cast:

void DebugLog::OnMessage(Message *msg)
{
    static DebugMsgData *debug;
    static XYZMsgData *xyz;

    if(debug = dynamic_cast<DebugMsgData*>(msg->pdata)){
        // debug message
    }
    else if(xyz = dynamic_cast<XYZMsgData*>(msg->pdata)){
        // xyz message
    }
    else/* if( ... )*/{
        // ...
    }
}

const_cast:

// *Passwd declared as a const

const unsigned char *Passwd


// on some situation it require to remove its constness

const_cast<unsigned char*>(Passwd)

reinterpret_cast:

typedef unsigned short uint16;

// Read Bytes returns that 2 bytes got read. 

bool ByteBuffer::ReadUInt16(uint16& val) {
  return ReadBytes(reinterpret_cast<char*>(&val), 2);
}

151
2017-12-11 02:05



Potrebbe aiutarti se conosci un po 'di elementi interni ...

static_cast

  • Il compilatore C ++ sa già come convertire i tipi di scaler come float in int. Usa static_cast per loro.
  • In generale, durante la conversione di tipo A in B, static_cast chiamerebbe il costruttore di B passandolo A. Se B non dispone di tale costruttore, si ottiene l'errore del tempo di compilazione.
  • Trasmetti da A* a B* sempre succede se A e B sono nella gerarchia di ereditarietà (o nulla) altrimenti si ottiene errore di compilazione.
  • Gotcha: Se si trasmette il puntatore di base al puntatore derivato, ma se l'oggetto effettivo è se non è un tipo derivato, allora si non ottieni un errore Si ottiene un puntatore non valido e non appena si tenta di accedere ai membri del puntatore derivato, si ottiene segfault in fase di esecuzione.
  • Lo stesso vale per A& a B&.
  • Gotcha: Trasmetti da Derivato a Base o viceversa crea una nuova copia! Per le persone che provengono da C # / Java, molte di queste possono essere una grande sorpresa.

dynamic_cast

  • dynamic_cast utilizza le informazioni sul tipo di runtime per capire se il cast è valido. Per esempio, (Base*) a (Derived*)potrebbe non riuscire se il puntatore non è effettivamente di tipo derivato.
  • Ciò significa che dynamic_cast è molto costoso rispetto a static_cast!
  • Per A* a B*, se cast non è valido allora dynamic_cast restituirà nullptr.
  • Per A& a B& se il cast non è valido, dynamic_cast genererà un'eccezione bad_cast.
  • A differenza di altri cast, esiste un sovraccarico di runtime.

const_cast

  • Mentre static_cast può fare non const per const, non può andare diversamente. Const_cast può fare entrambe le cose.
  • Un esempio in cui questo è utile sta iterando attraverso alcuni contenitori come set<T> che restituisce solo i suoi elementi come const per assicurarsi di non cambiare la sua chiave. Tuttavia, se il tuo intento è modificare i membri non chiave dell'oggetto, allora dovrebbe essere ok. Puoi usare const_cast per rimuovere constness.
  • Un altro esempio è quando si desidera implementare T& foo() così come const T& foo(). Per evitare la duplicazione del codice, puoi applicare const_cast per restituire il valore di una funzione da un'altra.

reinterpret_cast

  • Questo in pratica dice che prendi questi byte in questa posizione di memoria e pensaci come un dato oggetto.
  • Ad esempio, puoi caricare 4 byte di float a 4 byte di int per vedere come appaiono i bit in float.
  • Ovviamente, se i dati non sono corretti per il tipo, potresti ottenere un segfault.
  • Non ci sono sovraccarichi di runtime per questo cast.

51
2017-12-01 20:20



fa Questo rispondi alla tua domanda?

Non ho mai usato reinterpret_caste chiedersi se imbattersi in un caso che ne ha bisogno non è un odore di cattivo design. Nel codice base su cui lavoro dynamic_cast è usato molto La differenza con static_cast è quello a dynamic_cast esegue il controllo runtime che può (più sicuro) o meno (più in alto) essere quello che vuoi (vedi msdn).


11
2018-05-31 14:16



Oltre alle altre risposte finora, ecco un esempio non ovvio in cui static_cast non è sufficiente così reinterpret_cast è necessario. Supponiamo che esista una funzione che in un parametro di output restituisce puntatori a oggetti di classi diverse (che non condividono una classe base comune). Un vero esempio di tale funzione è CoCreateInstance() (vedi l'ultimo parametro, che è in effetti void**). Si supponga di richiedere una particolare classe di oggetto da questa funzione, in modo da conoscere in anticipo il tipo per il puntatore (che si fa spesso per gli oggetti COM). In questo caso non puoi eseguire il cast del puntatore al tuo puntatore void** con static_cast: hai bisogno reinterpret_cast<void**>(&yourPointer).

Nel codice:

#include <windows.h>
#include <netfw.h>
.....
INetFwPolicy2* pNetFwPolicy2 = nullptr;
HRESULT hr = CoCreateInstance(__uuidof(NetFwPolicy2), nullptr,
    CLSCTX_INPROC_SERVER, __uuidof(INetFwPolicy2),
    //static_cast<void**>(&pNetFwPolicy2) would give a compile error
    reinterpret_cast<void**>(&pNetFwPolicy2) );

Però, static_cast funziona per semplici puntatori (non puntatori a puntatori), quindi il codice sopra può essere riscritto per evitare reinterpret_cast (al prezzo di una variabile extra) nel modo seguente:

#include <windows.h>
#include <netfw.h>
.....
INetFwPolicy2* pNetFwPolicy2 = nullptr;
void* tmp = nullptr;
HRESULT hr = CoCreateInstance(__uuidof(NetFwPolicy2), nullptr,
    CLSCTX_INPROC_SERVER, __uuidof(INetFwPolicy2),
    &tmp );
pNetFwPolicy2 = static_cast<INetFwPolicy2*>(tmp);

9