Care sunt utilizările corecte ale:
static_cast
dynamic_cast
const_cast
reinterpret_cast
(tip)valoare
type(value)
Cum se decide care dintre ele se folosește în anumite cazuri specifice?
static_cast
este prima aruncare pe care ar trebui să încercați să o utilizați. Acesta face lucruri precum conversii implicite între tipuri (cum ar fi int
în float
sau pointer în void*
) și poate, de asemenea, apela funcții de conversie explicite (sau implicite). În multe cazuri, declararea explicită a lui static_cast
nu este necesară, dar este important de reținut că sintaxa T(ceva)
este echivalentă cu (T)ceva
și trebuie evitată (mai multe despre asta mai târziu). Cu toate acestea, un T(ceva, ceva_altfel)
este sigur și garantează apelarea constructorului.
static_cast
poate, de asemenea, să facă o distribuție prin ierarhii de moștenire. Nu este necesar atunci când se face o distribuție ascendentă (către o clasă de bază), dar atunci când se face o distribuție descendentă, poate fi folosit atâta timp cât nu se face o distribuție prin moștenirea virtuală
. Cu toate acestea, nu face verificări și este un comportament nedefinit să se facă static_cast
în josul unei ierarhii către un tip care nu este de fapt tipul obiectului.
const_cast
poate fi utilizat pentru a elimina sau adăuga const
la o variabilă; nici un alt cast din C++ nu este capabil să îl elimine (nici măcar reinterpret_cast
). Este important de reținut că modificarea unei valori care a fost const
este nedefinită numai dacă variabila originală este const
; dacă o utilizați pentru a elimina const
dintr-o referință la ceva care nu a fost declarat cu const
, este sigur. Acest lucru poate fi util atunci când se supraîncarcă funcții membre bazate pe const
, de exemplu. De asemenea, poate fi folosit pentru a adăuga const
la un obiect, de exemplu pentru a apela o supraîncărcare a unei funcții membre.
const_cast
funcționează în mod similar și cu volatile
, deși acest lucru este mai puțin obișnuit.
dynamic_cast
este folosit exclusiv pentru a gestiona polimorfismul. Se poate cast un pointer sau o referință la orice tip polimorfic la orice alt tip de clasă (un tip polimorfic are cel puțin o funcție virtuală, declarată sau moștenită). Îl puteți utiliza pentru mai mult decât pentru a face casting în jos - puteți face cast în lateral sau chiar în susul unui alt lanț. dynamic_cast
va căuta obiectul dorit și îl va returna, dacă este posibil. Dacă nu poate, va returna nullptr
în cazul unui pointer, sau va arunca std::bad_cast
în cazul unei referințe.
Totuși, dynamic_cast
are unele limitări. Nu funcționează dacă există mai multe obiecte de același tip în ierarhia de moștenire (așa-numitul "diamant de temut") și dacă nu folosiți moștenirea virtuală
. De asemenea, poate trece numai prin moștenirea publică - întotdeauna nu va reuși să treacă prin moștenirea protejată
sau privată
. Cu toate acestea, acest lucru este rareori o problemă, deoarece astfel de forme de moștenire sunt rare.
reinterpret_cast
este cel mai periculos cast și ar trebui folosit foarte rar. Acesta transformă un tip direct în altul - cum ar fi turnarea valorii de la un pointer la altul, sau stocarea unui pointer într-un int
, sau tot felul de alte lucruri neplăcute. În mare parte, singura garanție pe care o aveți cu reinterpret_cast
este că, în mod normal, dacă transformați rezultatul înapoi în tipul original, veți obține exact aceeași valoare (dar nu dacă tipul intermediar este mai mic decât tipul original). Există o serie de conversii pe care reinterpret_cast
nu le poate face, de asemenea. Este utilizat în principal pentru conversii și manipulări de biți deosebit de ciudate, cum ar fi transformarea unui flux de date brute în date reale sau stocarea datelor în biții mici ai unui pointer la date aliniate.
C-style cast și function-style cast sunt caste care utilizează (tip)obiect
sau, respectiv, type(obiect)
și sunt echivalente din punct de vedere funcțional. Ele sunt definite ca fiind prima dintre următoarele care reușește:
const_cast
static_cast
(deși ignoră restricțiile de acces)static_cast
(a se vedea mai sus), apoi const_cast
.reinterpret_cast
reinterpret_cast
, apoi const_cast
.Prin urmare, poate fi folosit ca înlocuitor pentru alte caste în unele cazuri, dar poate fi extrem de periculos din cauza capacității de a se transforma într-un reinterpret_cast
, iar acesta din urmă ar trebui preferat atunci când este nevoie de o turnare explicită, cu excepția cazului în care sunteți sigur că static_cast
va reuși sau reinterpret_cast
va eșua. Chiar și atunci, luați în considerare opțiunea mai lungă și mai explicită.
Castările în stil C ignoră, de asemenea, controlul accesului atunci când efectuează un static_cast
, ceea ce înseamnă că au capacitatea de a efectua o operațiune pe care nicio altă cast nu o poate face. Totuși, acest lucru este în mare parte un artificiu și, după părerea mea, este doar un alt motiv pentru a evita castările în stil C.
Utilizați dynamic_cast
pentru a converti pointeri/referințe în cadrul unei ierarhii de moștenire.
Utilizați static_cast
pentru conversiile de tip obișnuite.
Utilizați reinterpret_cast
pentru reinterpretarea la nivel scăzut a modelelor de biți. A se utiliza cu mare precauție.
Utilizați const_cast
pentru a elimina const/volatile
. Evitați acest lucru, cu excepția cazului în care sunteți blocat folosind o API const-incorectă.
(Multe explicații teoretice și conceptuale au fost date mai sus)
Mai jos sunt prezentate câteva dintre exemplele practice în care am folosit static_cast, dynamic_cast, const_cast, reinterpret_cast.
(De asemenea, pentru a înțelege explicația, consultați acest lucru: 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);
}
Ar putea fi de ajutor dacă știi un pic de interior...
static_cast
static_cast
pentru acestea.A
în B
, static_cast
apelează constructorul lui B
, trecând A
ca parametru. Alternativ, A
ar putea avea un operator de conversie (de exemplu, A::operator B()
). Dacă B
nu are un astfel de constructor sau dacă A
nu are un operator de conversie, atunci se obține o eroare de compilare. A*
la B*
reușește întotdeauna dacă A și B se află în ierarhia de moștenire (sau în vid), în caz contrar se obține o eroare de compilare.A&
în B&
.dynamic_cast
(Base*)
în (Derived*)
poate eșua dacă pointerul nu este de fapt de tip derivat.A*
către B*
, dacă cast-ul nu este valid, atunci dynamic_cast va returna nullptr.A&
către B&
, dacă cast-ul nu este valid, atunci dynamic_cast va arunca excepția bad_cast.const_cast
set<T>
, care returnează numai elementele sale ca fiind const pentru a se asigura că nu se schimbă cheia. Cu toate acestea, dacă intenția dvs. este de a modifica membrii care nu sunt cheia obiectului, atunci ar trebui să fie în regulă. Puteți utiliza const_cast pentru a elimina constanța.T& SomeClass::foo()
precum și const T& SomeClass::foo() const
. Pentru a evita duplicarea codului, puteți aplica const_cast pentru a returna valoarea unei funcții din alta.reinterpret_cast
Răspunde aceasta la întrebarea dumneavoastră?
Nu am folosit niciodată reinterpret_cast
și mă întreb dacă nu cumva faptul că am dat peste un caz care are nevoie de el nu este un miros de proiectare proastă. În baza de cod la care lucrez, dynamic_cast
este folosit foarte mult. Diferența față de static_cast
este că un dynamic_cast
face verificări în timpul execuției, ceea ce poate fi (mai sigur) sau nu (mai multă suprasolicitare) ceea ce doriți (a se vedea msdn).
În plus față de celelalte răspunsuri de până acum, iată un exemplu neobservat în care static_cast
nu este suficient, astfel încât este nevoie de reinterpret_cast
. Să presupunem că există o funcție care, într-un parametru de ieșire, returnează pointeri la obiecte din clase diferite (care nu au o clasă de bază comună). Un exemplu real de astfel de funcție este CoCreateInstance()
(a se vedea ultimul parametru, care este de fapt void**
). Să presupunem că solicitați o anumită clasă de obiect de la această funcție, astfel încât să știți în avans tipul pentru pointer (ceea ce se întâmplă adesea în cazul obiectelor COM). În acest caz, nu puteți transforma pointerul în void**
cu static_cast
: aveți nevoie de reinterpret_cast<void**>(&yourPointer)
.
În cod:
#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) );
Cu toate acestea, static_cast
funcționează pentru pointeri simpli (nu pentru pointeri la pointeri), astfel încât codul de mai sus poate fi rescris pentru a evita reinterpret_cast
(cu prețul unei variabile suplimentare) în felul următor:
#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);
În timp ce alte răspunsuri au descris frumos toate diferențele dintre castelele C++, aș dori să adaug o scurtă notă de ce nu ar trebui să folosiți castelele în stil C (Type) var
și Type(var)
.
Pentru începătorii din C++, castelele în stil C par a fi operația superset peste castelele C++ (static_cast<>(), dynamic_cast<>(), const_cast<>(), reinterpret_cast<>()) și cineva le-ar putea prefera în locul castelor C++. De fapt, castul în stil C este supersetul și este mai scurt de scris.
Principala problemă a castelor în stil C este că acestea ascund intenția reală a dezvoltatorului. Castingurile în stil C pot efectua practic toate tipurile de casting, de la castinguri sigure în mod normal, realizate prin static_cast<>() și dynamic_cast<>(), până la castinguri potențial periculoase, cum ar fi const_cast<>(), unde modificatorul const poate fi eliminat, astfel încât variabilele const pot fi modificate și reinterpret_cast<>(), care poate chiar reinterpreta valori întregi în pointeri.
Iată exemplul.
int a=rand(); // Random number.
int* pa1=reinterpret_cast<int*>(a); // OK. Here developer clearly expressed he wanted to do this potentially dangerous operation.
int* pa2=static_cast<int*>(a); // Compiler error.
int* pa3=dynamic_cast<int*>(a); // Compiler error.
int* pa4=(int*) a; // OK. C-style cast can do such cast. The question is if it was intentional or developer just did some typo.
*pa4=5; // Program crashes.
Principalul motiv pentru care au fost adăugate în limbajul C++ a fost acela de a permite unui programator să își clarifice intențiile - de ce va face acea distribuție. Utilizând caste în stil C, care sunt perfect valabile în C++, vă faceți codul mai puțin lizibil și mai predispus la erori, în special pentru alți programatori care nu au creat codul dumneavoastră. Prin urmare, pentru a face codul mai ușor de citit și mai explicit, ar trebui să preferați întotdeauna castelele C++ în locul celor în stil C.
Iată un scurt citat din cartea lui Bjarne Stroustrup (autorul cărții C++) The C++ Programming Language, ediția a 4-a - pagina 302.
Acest cast în stil C este mult mai periculos decât operatorii de conversie numiți deoarece notația este mai greu de reperat într-un program mare, iar tipul de conversie dorit de programator nu este explicit.
Pentru a înțelege, să luăm în considerare fragmentul de cod de mai jos:
struct Foo{};
struct Bar{};
int main(int argc, char** argv)
{
Foo* f = new Foo;
Bar* b1 = f; // (1)
Bar* b2 = static_cast<Bar*>(f); // (2)
Bar* b3 = dynamic_cast<Bar*>(f); // (3)
Bar* b4 = reinterpret_cast<Bar*>(f); // (4)
Bar* b5 = const_cast<Bar*>(f); // (5)
return 0;
}
Numai linia (4) se compilează fără erori. Numai reinterpret_cast poate fi utilizat pentru a converti un pointer la un obiect într-un pointer la un tip de obiect care nu are legătură cu acesta.
Trebuie remarcat un lucru: dynamic_cast ar eșua în timpul execuției, însă la majoritatea compilatoarelor nu va reuși nici să compileze, deoarece nu există funcții virtuale în structura pointerului care este castat, ceea ce înseamnă că dynamic_cast va funcționa numai cu pointeri de clasă polimorfici.
Când se utilizează castul C++: