Vraag Gewone cast versus static_cast vs. dynamische_cast [dupliceren]


Deze vraag heeft hier al een antwoord:

Ik schrijf al bijna twintig jaar C- en C ++-code, maar er is één aspect van deze talen dat ik nooit echt heb begrepen. Ik heb duidelijk gewone casten gebruikt, d.w.z.

MyClass *m = (MyClass *)ptr;

overal, maar er lijken nog twee andere soorten afgietsels te zijn, en ik ken het verschil niet. Wat is het verschil tussen de volgende coderegels?

MyClass *m = (MyClass *)ptr;
MyClass *m = static_cast<MyClass *>(ptr);
MyClass *m = dynamic_cast<MyClass *>(ptr);

1468
2017-08-26 13:20


oorsprong


antwoorden:


static_cast

static_cast wordt gebruikt voor gevallen waarin u in principe een impliciete conversie wilt terugdraaien, met een paar beperkingen en toevoegingen. static_cast voert geen runtime-controles uit. Dit moet worden gebruikt als u weet dat u naar een object van een specifiek type verwijst en dat een controle daarom niet nodig is. Voorbeeld:

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();
}

In dit voorbeeld weet u dat u geslaagd bent voor a MyClass object, en dus is er geen noodzaak voor een runtime-controle om dit te garanderen.

dynamic_cast

dynamic_cast is handig als u niet weet wat het dynamische type van het object is. Het retourneert een lege aanwijzer als het object waarnaar wordt verwezen niet het type bevat dat is casted to als een basisklasse (wanneer u naar een referentie cast, een bad_cast uitzondering wordt in dat geval gegooid).

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

Je kunt niet gebruiken dynamic_cast als u downcastt (cast naar een afgeleide klasse) en het argumenttype is niet polymorf. De volgende code is bijvoorbeeld niet geldig, omdat Base bevat geen enkele virtuele functie:

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

Een "up-cast" (cast naar de basisklasse) is altijd geldig voor beide static_cast en dynamic_cast, en ook zonder cast, omdat een 'up-cast' een impliciete conversie is.

Regelmatig casten

Deze afgietsels worden ook cast in de C-stijl genoemd. Een cast in C-stijl is in principe identiek aan het uitproberen van een reeks reeksen C ++ -casts en het nemen van de eerste C ++ -cast die werkt, zonder ooit te overwegen dynamic_cast. Onnodig te zeggen dat dit veel krachtiger is omdat het alles combineert const_cast, static_cast en reinterpret_cast, maar het is ook onveilig, omdat het niet gebruikt dynamic_cast.

Bovendien laten casts in C-stijl je niet alleen toe om dit te doen, maar ze laten je ook toe om veilig te casten naar een privé-basisklasse, terwijl het "equivalent" static_cast sequence zou je daarvoor een compile-time error geven.

Sommige mensen geven de voorkeur aan C-stijl afgietsels vanwege hun beknoptheid. Ik gebruik ze alleen voor numerieke casts en gebruik de juiste C ++ -casts wanneer door de gebruiker gedefinieerde typen worden gebruikt, omdat ze een strengere controle bieden.


1413
2017-08-10 13:50



Statische cast

De statische cast voert conversies uit tussen compatibele typen. Het lijkt op de cast in C-stijl, maar is beperkter. De cast in C-stijl zou bijvoorbeeld toestaan ​​dat een integer pointer naar een char verwijst.

char c = 10;       // 1 byte
int *p = (int*)&c; // 4 bytes

Aangezien dit resulteert in een aanwijzer van 4 bytes die wijst naar 1 byte toegewezen geheugen, zal het schrijven naar deze aanwijzer een run-time fout veroorzaken of een aangrenzend geheugen overschrijven.

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

In tegenstelling tot de cast in C-stijl, laat de statische cast de compiler controleren of de pointer- en pointee-gegevenstypen compatibel zijn, waardoor de programmeur deze onjuiste pointertoewijzing tijdens de compilatie kan opvangen.

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

Bereken opnieuw casten

Om de aanwijzerconversie te forceren, op dezelfde manier als de cast in de C-stijl op de achtergrond doet, zou in plaats daarvan de reinterpret-cast worden gebruikt.

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

Deze cast verwerkt conversies tussen bepaalde niet-gerelateerde typen, zoals van het ene aanwijstype naar het andere incompatibele aanwijzertype. Het zal eenvoudigweg een binaire kopie van de data uitvoeren zonder het onderliggende bitpatroon te veranderen. Merk op dat het resultaat van een dergelijke bewerking op een laag niveau systeemspecifiek is en daarom niet draagbaar. Het moet met voorzichtigheid worden gebruikt als het niet helemaal kan worden voorkomen.

Dynamische cast

Deze wordt alleen gebruikt om objectaanwijzers en objectreferenties om te zetten in andere pointer- of referentietypen in de overervingshiërarchie. Het is de enige cast die ervoor zorgt dat het object waarnaar wordt verwezen, kan worden geconverteerd door een uitvoeringstest uit te voeren dat de aanwijzer verwijst naar een volledig object van het bestemmingstype. Om deze runtime-controle mogelijk te maken, moet het object polymorf zijn. Dat wil zeggen dat de klasse ten minste één virtuele functie moet definiëren of erven. Dit komt omdat de compiler alleen de benodigde informatie over het run-time type voor dergelijke objecten genereert.

Dynamische cast-voorbeelden

In het onderstaande voorbeeld wordt een MyChild-aanwijzer geconverteerd naar een MyBase-aanwijzer met behulp van een dynamische cast. Deze afgeleide naar basisconversie slaagt, omdat het object Child een compleet Base-object bevat.

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



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

In het volgende voorbeeld wordt geprobeerd een MyBase-aanwijzer naar een MyChild-aanwijzer te converteren. Aangezien het Base-object geen volledig Child-object bevat, mislukt deze aanwijzerconversie. Om dit aan te geven, retourneert de dynamische cast een lege aanwijzer. Dit biedt een handige manier om te controleren of een conversie geslaagd is tijdens de uitvoering.

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


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

Als een verwijzing wordt geconverteerd in plaats van een aanwijzer, mislukt de dynamische cast door een bad_cast-uitzondering te gooien. Dit moet worden afgehandeld met behulp van een try-catch-verklaring.

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

Dynamische of statische cast

Het voordeel van het gebruik van een dynamische cast is dat het de programmeur toestaat om te controleren of een conversie geslaagd is tijdens de uitvoering. Het nadeel is dat er een prestatiekosten zijn verbonden aan het uitvoeren van deze controle. Daarom zou het gebruik van een statische cast in het eerste voorbeeld de voorkeur hebben gehad, omdat een afgeleide conversie nooit zal mislukken.

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

In het tweede voorbeeld kan de conversie echter slagen of mislukken. Het zal mislukken als het MyBase-object een MyBase-instantie bevat en het zal slagen als het een MyChild-instantie bevat. In sommige situaties is dit misschien pas bekend na de uitvoering. Wanneer dit het geval is, is dynamische cast een betere keuze dan statische cast.

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

Als de basis-tot-afgeleide conversie was uitgevoerd met een statische cast in plaats van een dynamische cast, zou de conversie niet zijn mislukt. Het zou een aanwijzer hebben teruggestuurd die verwijst naar een incompleet object. Het afleiden van een dergelijke aanwijzer kan leiden tot runtime-fouten.

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

// Incomplete MyChild object dereferenced
(*child);

Const cast

Deze wordt voornamelijk gebruikt om de const-modifier van een variabele toe te voegen of te verwijderen.

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

Hoewel met Const Cast de waarde van een constante kan worden gewijzigd, is dit nog steeds een ongeldige code die een runtimefout kan veroorzaken. Dit kan bijvoorbeeld gebeuren als de constante zich in een sectie van alleen-lezen geheugen bevond.

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

Const-cast wordt in plaats daarvan voornamelijk gebruikt wanneer er een functie is die een niet-constant pointerargument opneemt, ook al wijzigt het de pointee niet.

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

De functie kan vervolgens een constante variabele worden doorgegeven door een const-cast te gebruiken.

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

print(nonConst); // allowed

Bron en meer uitleg


116
2017-08-26 13:28



Je zou naar het artikel moeten kijken C ++ Programming / Type Casting.

Het bevat een goede beschrijving van alle verschillende cast-typen. Het volgende is overgenomen van de bovenstaande link:

const_cast

const_cast (expressie) De const_cast <> () wordt gebruikt om toe te voegen / te verwijderen   const (heid) (of vluchtigheid) van een variabele.

static_cast

static_cast (expressie) De static_cast <> () wordt gebruikt om tussen te casten   de integer types. 'B.v.' char-> long, int-> short etc.

Statische cast wordt ook gebruikt om pointers naar verwante typen te casten   bijvoorbeeld casting void * naar het juiste type.

dynamic_cast

Dynamische cast wordt gebruikt om pointers en referenties tijdens runtime om te zetten,   meestal met het doel om een ​​aanwijzer of referentie omhoog of omlaag te werpen   een overervingsketen (overervingshiërarchie).

dynamic_cast (expressie)

Het doeltype moet een aanwijzer of een verwijzingstype zijn en de   expressie moet evalueren naar een aanwijzer of referentie. Dynamische cast werkt   alleen wanneer het type object waarnaar de expressie verwijst, is   compatibel met het doeltype en de basisklasse heeft er ten minste één   virtuele led-functie. Zo niet, en het type uitdrukking dat wordt gegoten   is een aanwijzer, NULL wordt geretourneerd, als een dynamische cast een referentie oplevert   mislukt, er wordt een bad_cast-uitzondering gegenereerd. Wanneer het niet faalt, dynamisch   cast retourneert een aanwijzer of verwijzing van het doeltype naar het object   naar welke uitdrukking verwezen.

reinterpret_cast

Bij het interpreteren van cast wordt het ene type bitwise naar het andere type gegoten. Elke aanwijzer   of integraal type kan naar elk ander worden gegoten met cast cast opnieuw,   gemakkelijk toe te staan ​​voor misbruik. Bijvoorbeeld, herinterpreteer er een   kan, onveilig, een geheel getal aanwijzer naar een stringaanwijzer plaatsen.


71
2017-09-19 17:30



Vermijd het gebruik van casts in C-stijl.

Casting in C-stijl is een mix van const en cast opnieuw interpreteren, en het is moeilijk te vinden en te vervangen in je code. Een C ++ -applicatieprogrammeur moet C-stijl cast vermijden.


23
2017-08-26 13:39



Ter informatie, ik geloof dat Bjarne Stroustrup wordt geciteerd als te zeggen dat casten in C-stijl moeten worden vermeden en dat je static_cast of dynamic_cast moet gebruiken als het enigszins mogelijk is.

Barne Stroustrup's Veelgestelde vragen over C ++ -stijl

Neem dat advies voor wat je wilt. Ik ben helemaal geen C ++ -goeroe.


21
2017-08-26 13:38



C-style casts samenvoegen const_cast, static_cast en reinterpret_cast.

Ik wou dat C ++ geen C-stijl casts had. C ++ -casts vallen op de juiste manier op (zoals ze zouden moeten zijn, casts zijn normaal gesproken een indicatie voor iets slechts) en maken een goed onderscheid tussen de verschillende soorten conversie die casts uitvoeren. Ze laten ook toe dat vergelijkbare uitziende functies worden geschreven, b.v. boost :: lexical_cast, wat erg leuk is vanuit een consistent perspectief.


10
2017-08-26 13:26



dynamic_castheeft runtime type checking en werkt alleen met verwijzingen en pointers, terwijl static_cast biedt geen runtime-typecontrole. Zie het MSDN-artikel voor volledige informatie static_cast Operator.


8
2018-02-05 17:10



dynamic_cast ondersteunt alleen aanwijzer- en referentietypes. Het komt terug NULL als de cast onmogelijk is als het type een aanwijzer is of een uitzondering genereert als het type een referentietype is. Vandaar, dynamic_cast kan worden gebruikt om te controleren of een object van een bepaald type is, static_cast kan niet (u krijgt gewoon een ongeldige waarde).

C-stijl (en andere) afgietsels zijn behandeld in de andere antwoorden.


7