Vraag Wanneer moeten static_cast, dynamic_cast, const_cast en reinterpret_cast worden gebruikt?


Wat zijn de juiste toepassingen van:

  • static_cast
  • dynamic_cast
  • const_cast
  • reinterpret_cast
  • C-stijl cast (type)value
  • Functioneel cast type(value)

Hoe beslist men welke te gebruiken in welke specifieke gevallen?


2048
2017-12-01 20:11


oorsprong


antwoorden:


static_cast is de eerste cast die je zou moeten gebruiken. Het doet dingen als impliciete conversies tussen typen (zoals int naar float, of aanwijzer naar void*), en het kan ook expliciete conversiefuncties (of impliciete) oproepen. In veel gevallen expliciet vermelden static_cast is niet noodzakelijk, maar het is belangrijk op te merken dat de T(something) syntaxis is gelijk aan (T)something en moet worden vermeden (daarover later meer). EEN T(something, something_else) is echter veilig en zal gegarandeerd de constructeur bellen.

static_cast kan ook casten via overervingshiërarchieën. Het is niet nodig om naar boven te gieten (naar een basisklasse), maar als het naar beneden wordt gegoten, kan het worden gebruikt zolang het niet doorboort virtual erfenis. Het controleert echter niet en het is ongedefinieerd gedrag voor static_cast door een hiërarchie naar een type dat niet echt het type object is.


const_cast kan worden gebruikt om te verwijderen of toe te voegen const naar een variabele; geen andere C ++ -cast kan deze verwijderen (zelfs niet reinterpret_cast). Het is belangrijk om op te merken dat het wijzigen van een voorheen const waarde is alleen ongedefinieerd als de oorspronkelijke variabele is const; als je het gebruikt om de const een verwijzing naar iets dat niet is verklaard met const, het is veilig. Dit kan handig zijn bij het overbelasten van lidfuncties op basis van const, bijvoorbeeld. Het kan ook worden gebruikt om toe te voegen const naar een object, zoals overbelasting van de ledfunctie.

const_cast werkt ook op dezelfde manier volatilehoewel dat minder gebruikelijk is.


dynamic_cast wordt bijna uitsluitend gebruikt voor het omgaan met polymorfisme. U kunt een aanwijzer of verwijzing naar een willekeurig type naar een ander klassentype casten (een polymorf type heeft ten minste één virtuele functie, gedeclareerd of overgenomen). Je kunt het gebruiken voor meer dan alleen naar beneden gieten - je kunt zijdelings of zelfs een andere ketting omhoog schieten. De dynamic_cast zal het gewenste object opzoeken en indien mogelijk retourneren. Als het niet kan, zal het terugkeren nullptr in het geval van een aanwijzer of worp std::bad_cast in het geval van een referentie.

dynamic_cast heeft echter een aantal beperkingen. Het werkt niet als er meerdere objecten van hetzelfde type in de overervingshiërarchie zijn (de zogenaamde 'gevreesde diamant') en u niet gebruikt virtual erfenis. Het kan ook alleen door het openbaar erfgoed gaan - het zal altijd falen om door te reizen protected of private erfenis. Dit is echter zelden een probleem, omdat dergelijke vormen van overerving zeldzaam zijn.


reinterpret_cast is de meest gevaarlijke cast, en zou zeer spaarzaam gebruikt moeten worden. Het verandert het ene type rechtstreeks in het andere - zoals het gieten van de waarde van de ene aanwijzer naar de andere, of het opslaan van een aanwijzer in een int, of allerlei andere vervelende dingen. Grotendeels, de enige garantie die u krijgt reinterpret_cast is dat normaal als je het resultaat terugzet naar het originele type, je exact dezelfde waarde krijgt (maar niet als het tussentype kleiner is dan het originele type). Er zijn een aantal conversies die reinterpret_cast kan ook niet doen. Het wordt voornamelijk gebruikt voor bijzonder rare conversies en bitmanipulaties, zoals het omzetten van een onbewerkte gegevensstroom in werkelijke gegevens of het opslaan van gegevens in de lage bits van een uitgelijnde aanwijzer.


C-stijl cast en function-style cast zijn afgietsels met (type)object of type(object), respectievelijk. Een cast in C-stijl wordt gedefinieerd als de eerste van de volgende die slaagt:

  • const_cast
  • static_cast (hoewel toegangsbeperkingen worden genegeerd)
  • static_cast (zie hierboven), dan const_cast
  • reinterpret_cast
  • reinterpret_cast, dan const_cast

Het kan daarom in sommige gevallen worden gebruikt als vervanging voor andere afgietsels, maar kan zeer gevaarlijk zijn vanwege het vermogen om over te gaan naar een reinterpret_cast, en dit laatste zou de voorkeur moeten krijgen als expliciet gieten nodig is, tenzij je het zeker weet static_cast zal slagen of reinterpret_cast zal mislukken. Overweeg dan zelfs de langere, meer expliciete optie.

Gieten in C-stijl negeert ook de toegangscontrole bij het uitvoeren van een static_cast, wat betekent dat ze de mogelijkheid hebben om een ​​bewerking uit te voeren die geen andere cast kan. Dit is meestal een kladje en in mijn gedachten is dit gewoon een andere reden om C-stijl afgietsels te vermijden.


2207
2017-12-01 20:22



Gebruik dynamic_cast voor het converteren van aanwijzers / verwijzingen binnen een overervingshiërarchie.

Gebruik static_cast voor gewone typeconversies.

Gebruik reinterpret_cast voor low-level herinterpretatie van bitpatronen. Gebruik met uiterste voorzichtigheid.

Gebruik const_cast voor wegwerpen const/volatile. Vermijd dit tenzij u vastzit met een const-incorrecte API.


283
2018-01-21 04:53



(Er is hierboven veel theoretische en conceptuele uitleg gegeven) 

Hieronder staan ​​enkele van de praktische voorbeelden toen ik gebruikte static_cast, dynamic_cast, const_cast, reinterpret_cast.

(Verwijst ook dit om de uitleg te begrijpen: 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);
  .....
}

dynamische_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



Het kan helpen als je een beetje internals kent ...

static_cast

  • C ++ - compiler weet al hoe scaler-typen zoals float naar int geconverteerd moeten worden. Gebruik static_cast voor hen.
  • Over het algemeen, tijdens het converteren van type A naar B, zou static_cast de constructor van B de A laten doorgeven. Als B niet zo'n constructor heeft, dan krijg je een compile time error.
  • Cast uit A* naar B* altijd slaagt als A en B in overervingshiërarchie (of ongeldig) zijn, anders krijg je een compileerfout.
  • Gotcha: Als u de basisaanwijzer naar de afgeleide aanwijzer baseert, maar als het werkelijke object is als het niet is afgeleid, typt u niet doen krijg foutmelding. Je krijgt een slechte pointer en zodra je toegang probeert te krijgen tot leden van de afgeleide pointer, krijg je segfault tijdens runtime.
  • Hetzelfde geldt voor A& naar B&.
  • Gotcha: Cast from Derived to Base of viceversa maakt een nieuw exemplaar! Voor mensen die van C # / Java komen, kunnen veel van bovenstaande een enorme verrassing zijn.

dynamic_cast

  • dynamic_cast gebruikt runtime-typegegevens om te bepalen of cast geldig is. Bijvoorbeeld, (Base*) naar (Derived*)kan falen als de aanwijzer niet van het afgeleide type is.
  • Dit betekent dat dynamic_cast erg duur is in vergelijking met static_cast!
  • Voor A* naar B*, als cast ongeldig is, retourneert dynamic_cast nullptr.
  • Voor A& naar B& als cast ongeldig is, zal dynamic_cast de bad_cast-uitzondering weggooien.
  • In tegenstelling tot andere casts, is er runtime overhead.

const_cast

  • Hoewel static_cast niet-const kan doen om te constateren, kan het niet andersom gaan. De const_cast kan beide kanten op.
  • Een voorbeeld waarbij dit handig is, is het itereren door een container zoals set<T> waarbij alleen de elementen als const worden geretourneerd om ervoor te zorgen dat u de sleutel niet wijzigt. Als het echter uw bedoeling is om de niet-hoofdleden van het object te wijzigen, moet dit in orde zijn. U kunt const_cast gebruiken om constance te verwijderen.
  • Een ander voorbeeld is wanneer u wilt implementeren T& foo() net zoals const T& foo(). Om codeduplicatie te voorkomen, kunt u const_cast toepassen om de waarde van de ene functie van de andere te retourneren.

reinterpret_cast

  • Dit zegt in feite dat deze bytes op deze geheugenlocatie worden genomen en beschouw het als gegeven object.
  • U kunt bijvoorbeeld 4 bytes float laden naar 4 bytes int om te zien hoe bits in float eruitzien.
  • Het is duidelijk dat als de gegevens niet correct zijn voor het type, u mogelijk een segfault krijgt.
  • Er is geen runtime overhead voor deze cast.

51
2017-12-01 20:20



Doet deze je vraag beantwoorden?

Ik heb nooit gebruikt reinterpret_casten vraag me af of het tegenkomen van een zaak die het nodig heeft, geen geur is van slecht ontwerp. In de code base werk ik aan dynamic_cast wordt veel gebruikt. Het verschil met static_cast is dat een dynamic_cast controleert of runtime controleert wat (veiliger) of niet (meer overhead) is wat u wilt (zie msdn).


11
2018-05-31 14:16



In aanvulling op de andere antwoorden tot nu toe, hier is niet voor de hand liggende voorbeeld waar static_cast is niet voldoende dus dat reinterpret_cast is nodig. Stel dat er een functie is die in een uitvoerparameter pointers retourneert naar objecten van verschillende klassen (die geen gemeenschappelijke basisklasse delen). Een echt voorbeeld van een dergelijke functie is CoCreateInstance() (zie de laatste parameter, die in feite is void**). Stel dat u een bepaalde klasse van objecten uit deze functie opvraagt, zodat u van tevoren weet welk type voor de aanwijzer wordt gebruikt (wat u vaak doet voor COM-objecten). In dit geval kunt u geen wijzer naar uw aanwijzer plaatsen void** met static_cast: jij hebt nodig reinterpret_cast<void**>(&yourPointer).

In code:

#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) );

Echter, static_cast werkt voor eenvoudige aanwijzers (geen verwijzingen naar aanwijzers), dus de bovenstaande code kan worden herschreven om te voorkomen reinterpret_cast (tegen een prijs van een extra variabele) op de volgende manier:

#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