Almindelig cast vs. static_cast vs. dynamic_cast

Almindelig cast vs. static_cast vs. dynamic_cast

static_cast

static_cast bruges til tilfælde, hvor du grundlæggende ønsker at vende en implicit konvertering, med et par begrænsninger og tilføjelser. static_cast udfører ingen køretidstjek. Dette skal bruges, hvis du ved, at du refererer til et objekt af en bestemt type, og derfor ville en kontrol være unødvendig. Eksempel:

void func(void *data) {
  // Conversion from MyClass* -> void* is implicit
  MyClass *c = static_cast<MyClass*>(data);
  ...
}

int main() {
  MyClass c;
  start_thread(&func, &c)  // func(&c) will be called
      .join();
}

I dette eksempel ved du, at du har bestået en MyClass objekt, og der er derfor ikke behov for et runtime-tjek for at sikre dette.

dynamic_cast

dynamic_cast er nyttig, når du ikke ved, hvad den dynamiske type af objektet er. Det returnerer en null-pointer, hvis objektet der henvises til ikke indeholder typen castet til som en basisklasse (når du caster til en reference, en bad_cast undtagelse er kastet i det tilfælde).

if (JumpStm *j = dynamic_cast<JumpStm*>(&stm)) {
  ...
} else if (ExprStm *e = dynamic_cast<ExprStm*>(&stm)) {
  ...
}

Du kan ikke bruge dynamic_cast hvis du nedkaster (caster til en afledt klasse), og argumenttypen ikke er polymorf. For eksempel er følgende kode ikke gyldig, fordi Base indeholder ingen virtuel funktion:

struct Base { };
struct Derived : Base { };
int main() {
  Derived d; Base *b = &d;
  dynamic_cast<Derived*>(b); // Invalid
}

En "up-cast" (cast til basisklassen) er altid gyldig med både static_cast og dynamic_cast , og også uden nogen rollebesætning, da en "up-cast" er en implicit konvertering.

Almindelig cast

Disse casts kaldes også C-style cast. Et cast i C-stil er grundlæggende identisk med at prøve en række sekvenser af C++-casts og tage det første C++-cast, der virker, uden nogensinde at overveje dynamic_cast . Det er overflødigt at sige, at dette er meget mere kraftfuldt, da det kombinerer alle const_cast , static_cast og reinterpret_cast , men det er også usikkert, fordi det ikke bruger dynamic_cast .

Derudover giver C-style casts dig ikke kun mulighed for at gøre dette, men de giver dig også mulighed for sikkert at kaste til en privat base-class, mens den "ækvivalente" static_cast sekvens ville give dig en kompileringsfejl for det.

Nogle mennesker foretrækker afstøbninger i C-stil på grund af deres korthed. Jeg bruger dem kun til numeriske casts og bruger de relevante C++ casts, når brugerdefinerede typer er involveret, da de giver strengere kontrol.


Statisk cast

Den statiske cast udfører konverteringer mellem kompatible typer. Det ligner C-stil rollebesætningen, men er mere restriktiv. For eksempel ville C-stil-castet tillade en heltalsmarkør at pege på et tegn.
char c = 10;       // 1 byte
int *p = (int*)&c; // 4 bytes

Da dette resulterer i en 4-byte pointer, der peger på 1 byte af allokeret hukommelse, vil skrivning til denne pointer enten forårsage en runtime fejl eller vil overskrive noget tilstødende hukommelse.

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

I modsætning til castet i C-stil vil det statiske cast give compileren mulighed for at kontrollere, at pointer- og pointee-datatyperne er kompatible, hvilket gør det muligt for programmøren at fange denne forkerte pointertildeling under kompilering.

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

Genfortolk castet

For at fremtvinge markørkonverteringen, på samme måde som C-stil-castet gør i baggrunden, ville genfortolkningen blive brugt i stedet.

int *r = reinterpret_cast<int*>(&c); // forced conversion

Denne cast håndterer konverteringer mellem visse ikke-relaterede typer, såsom fra én pointertype til en anden inkompatibel pointertype. Det vil simpelthen udføre en binær kopi af dataene uden at ændre det underliggende bitmønster. Bemærk, at resultatet af en operation på lavt niveau er systemspecifik og derfor ikke bærbar. Det skal bruges med forsigtighed, hvis det ikke kan undgås helt.

Dynamisk rollebesætning

Denne bruges kun til at konvertere objektpointere og objektreferencer til andre pointer- eller referencetyper i arvehierarkiet. Det er den eneste cast, der sørger for, at det objekt, der peges på, kan konverteres, ved at udføre en run-time-kontrol af, at markøren refererer til et komplet objekt af destinationstypen. For at denne køretidskontrol skal være mulig, skal objektet være polymorf. Det vil sige, at klassen skal definere eller arve mindst én virtuel funktion. Dette er fordi compileren kun vil generere den nødvendige runtime type information for sådanne objekter.

Eksempler på dynamiske rollebesætninger

I eksemplet nedenfor konverteres en MyChild pointer til en MyBase pointer ved hjælp af en dynamisk cast. Denne afledte-til-base-konvertering lykkes, fordi Child-objektet indeholder et komplet Base-objekt.

class MyBase 
{ 
  public:
  virtual void test() {}
};
class MyChild : public MyBase {};



int main()
{
  MyChild *child = new MyChild();
  MyBase  *base = dynamic_cast<MyBase*>(child); // ok
}

Det næste eksempel forsøger at konvertere en MyBase-markør til en MyChild-markør. Da Base-objektet ikke indeholder et komplet Child-objekt, vil denne pointerkonvertering mislykkes. For at indikere dette returnerer den dynamiske cast en nul-pointer. Dette giver en bekvem måde at kontrollere, om en konvertering er lykkedes eller ej under kørslen.

MyBase  *base = new MyBase();
MyChild *child = dynamic_cast<MyChild*>(base);

 
if (child == 0) 
std::cout << "Null pointer returned";

Hvis en reference konverteres i stedet for en pointer, vil den dynamiske cast fejle ved at kaste en bad_cast-undtagelse. Dette skal håndteres ved hjælp af en try-catch-sætning.

#include <exception>
// …  
try
{ 
  MyChild &child = dynamic_cast<MyChild&>(*base);
}
catch(std::bad_cast &e) 
{ 
  std::cout << e.what(); // bad dynamic_cast
}

Dynamisk eller statisk cast

Fordelen ved at bruge en dynamisk cast er, at den giver programmøren mulighed for at kontrollere, om en konvertering er lykkedes eller ej under kørsel. Ulempen er, at der er en præstationsomkostning forbundet med at udføre denne kontrol. Af denne grund ville det have været at foretrække at bruge en statisk cast i det første eksempel, fordi en afledt-til-base-konvertering aldrig vil fejle.

MyBase *base = static_cast<MyBase*>(child); // ok

Men i det andet eksempel kan konverteringen enten lykkes eller mislykkes. Det vil mislykkes, hvis MyBase-objektet indeholder en MyBase-instans, og det vil lykkes, hvis det indeholder en MyChild-instans. I nogle situationer er dette muligvis ikke kendt før runtime. Når dette er tilfældet, er dynamisk cast et bedre valg end statisk cast.

// Succeeds for a MyChild object
MyChild *child = dynamic_cast<MyChild*>(base);

Hvis den base-til-afledte konvertering var blevet udført ved hjælp af en statisk cast i stedet for en dynamisk cast, ville konverteringen ikke have fejlet. Det ville have returneret en pointer, der refererede til et ufuldstændigt objekt. Hvis en sådan pointer fravælges, kan det føre til køretidsfejl.

// Allowed, but invalid
MyChild *child = static_cast<MyChild*>(base);
 
// Incomplete MyChild object dereferenced
(*child);

Konst cast

Denne bruges primært til at tilføje eller fjerne const-modifikatoren for en variabel.

const int myConst = 5;
int *nonConst = const_cast<int*>(&myConst); // removes const

Selvom const cast tillader, at værdien af ​​en konstant ændres, er det stadig en ugyldig kode, der kan forårsage en køretidsfejl. Dette kunne for eksempel forekomme, hvis konstanten var placeret i en sektion af skrivebeskyttet hukommelse.

*nonConst = 10; // potential run-time error

Const cast bruges i stedet hovedsageligt, når der er en funktion, der tager et ikke-konstant pointer-argument, selvom det ikke ændrer pointee.

void print(int *p) 
{
   std::cout << *p;
}

Funktionen kan derefter overføres til en konstant variabel ved at bruge en const cast.

print(&myConst); // error: cannot convert 
                 // const int* to int*
 
print(nonConst); // allowed

Kilde og flere forklaringer


Du bør se på artiklen C++ Programmering/Typecasting .

Den indeholder en god beskrivelse af alle de forskellige cast-typer. Følgende er taget fra ovenstående link: