it-swarm-eu.dev

Proč použít static_cast <int> (x) namísto (int) x?

Slyšel jsem, že funkce static_cast by měla být upřednostňována před castingem ve stylu C nebo jednoduchým castingem ve stylu funkce. Je to pravda? Proč?

598
Tommy Herbert

Hlavním důvodem je to, že klasické C obsazení nerozlišuje mezi tím, co nazýváme static_cast<>(), reinterpret_cast<>(), const_cast<>() a dynamic_cast<>(). Tyto čtyři věci jsou úplně jiné.

static_cast<>() je obvykle bezpečná. Existuje platná konverze v jazyce nebo vhodný konstruktor, který to umožňuje. Jediný čas, kdy je to trochu riskantní, je, když sesadíte do zděděné třídy; musíte se ujistit, že objekt je vlastně potomek, o kterém tvrdíte, že je prostředkem k jazyku (jako příznak v objektu). dynamic_cast<>() je bezpečná, pokud je výsledek zkontrolován (ukazatel) nebo je zohledněna možná výjimka (reference).

A reinterpret_cast<>() (nebo const_cast<>()) na druhé straně je vždy nebezpečný. Řeknete kompilátoru: "Věřte mi: Vím, že to nevypadá jako foo (vypadá to, že není zaměnitelné), ale je".

Prvním problémem je, že je téměř nemožné říci, který z nich se objeví v obsazení ve stylu C, aniž bychom se dívali na velké a rozptýlené kusy kódu a neznali všechna pravidla.

Předpokládejme tyto:

class CDerivedClass : public CMyBase {...};
class CMyOtherStuff {...} ;

CMyBase  *pSomething; // filled somewhere

Nyní jsou tyto dva kompilovány stejným způsobem:

CDerivedClass *pMyObject;
pMyObject = static_cast<CDerivedClass*>(pSomething); // Safe; as long as we checked

pMyObject = (CDerivedClass*)(pSomething); // Same as static_cast<>
                                     // Safe; as long as we checked
                                     // but harder to read

Uvidíme však tento téměř identický kód:

CMyOtherStuff *pOther;
pOther = static_cast<CMyOtherStuff*>(pSomething); // Compiler error: Can't convert

pOther = (CMyOtherStuff*)(pSomething);            // No compiler error.
                                                  // Same as reinterpret_cast<>
                                                  // and it's wrong!!!

Jak vidíte, neexistuje snadný způsob, jak rozlišit mezi těmito dvěma situacemi, aniž by toho bylo mnoho známo o všech zúčastněných třídách.

Druhým problémem je, že obsazení ve stylu C je příliš těžké najít. Ve složitých výrazech může být velmi těžké vidět obsazení ve stylu C. Je prakticky nemožné napsat automatizovaný nástroj, který potřebuje najít obsazení ve stylu C (například vyhledávací nástroj) bez plně rozvinutého front-endu kompilátoru C++. Na druhé straně je snadné vyhledat výraz „static_cast <“ nebo „reinterpret_cast <“.

pOther = reinterpret_cast<CMyOtherStuff*>(pSomething);
      // No compiler error.
      // but the presence of a reinterpret_cast<> is 
      // like a Siren with Red Flashing Lights in your code.
      // The mere typing of it should cause you to feel VERY uncomfortable.

To znamená, že nejenomže jsou nebezpečnější obsazení ve stylu C, ale je mnohem těžší najít je všechny, abyste se ujistili, že jsou správné.

586
Euro Micelli

Jeden pragmatický tip: pokud plánujete uklidit projekt, můžete ve zdrojovém kódu snadno vyhledat klíčové slovo static_cast.

109
Karl

Stručně :

  1. static_cast<>() vám dává schopnost kompilace času zkontrolovat, C-Style obsazení ne.
  2. static_cast<>() lze snadno najít kdekoli uvnitř zdrojového kódu C++; na rozdíl od toho je obsazení C_Style obtížnější najít.
  3. Záměry jsou zprostředkovány mnohem lépe pomocí obsazení C++.

Další vysvětlení :

Statické obsazení provádí převody mezi kompatibilními typy . Je to podobné obsazení ve stylu C, ale je přísnější. Například obsazení ve stylu C by umožnilo celočíselnému ukazateli ukazovat na znak.

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

Protože to má za následek 4-bajtový ukazatel směřující na 1 bajt alokované paměti, zápis do tohoto ukazatele způsobí buď chybu běhu, nebo přepíše některou sousední paměť.

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

Na rozdíl od obsazení ve stylu C umožní statický obsazení kompilátoru zkontrolovat, zda jsou datové typy ukazatele a pointee kompatibilní, což umožňuje programátorovi zachytit toto nesprávné přiřazení ukazatele během kompilace.

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

Přečtěte si více v:
Jaký je rozdíl mezi castingem static_cast <> a C)
a
Pravidelné obsazení vs. static_cast vs. dynamic_cast

68
Breeze

Otázka je větší než pouhé použití wither static_cast nebo casting ve stylu C, protože při použití obsazení ve stylu C se dějí různé věci. Účelem operátorů castingu C++ je, aby tyto operace byly explicitnější.

Na povrchu se static_cast a C styl obsazení zdají stejné, například při obsazení jedné hodnoty na druhou:

int i;
double d = (double)i;                  //C-style cast
double d2 = static_cast<double>( i );  //C++ cast

Oba tito hodnotu celého čísla zdvojnásobí. Při práci s ukazateli se však situace komplikuje. nějaké příklady:

class A {};
class B : public A {};

A* a = new B;
B* b = (B*)a;                                  //(1) what is this supposed to do?

char* c = (char*)new int( 5 );                 //(2) that weird?
char* c1 = static_cast<char*>( new int( 5 ) ); //(3) compile time error

V tomto příkladu (1) možná OK, protože objekt, na který odkazuje A, je ve skutečnosti instancí B. Ale co když v tom okamžiku nevíte, na co vlastně odkazuje? (2) možná dokonale legální (chcete se pouze podívat na jeden bajt celého čísla), ale může to být také chyba, v takovém případě by byla chyba Nice, jako (3). Účelem operátorů castingu C++ je odhalit tyto problémy v kódu poskytováním chyb při kompilaci nebo běhu, pokud je to možné.

Takže pro přísné „odlévání hodnot“ můžete použít static_cast. Pokud chcete run-time polymorfní casting ukazatelů, použijte dynamic_cast. Pokud opravdu chcete zapomenout na typy, můžete použít reintrepret_cast. A právě vyhodit const z okna je const_cast.

Prostě to kód zpřesní, aby to vypadalo, že víte, co jste dělali.

27
Dusty Campbell

static_cast znamená, že nemůžete náhodně const_cast nebo reinterpret_cast, což je dobrá věc.

24
DrPizza
  1. Umožňuje snadno najít obsazení v kódu pomocí grepu nebo podobných nástrojů.
  2. Je to výslovně, jaký druh obsazení děláte, a zapojení kompilátorovy pomoci při vymáhání. Pokud chcete pouze odhazovat konstantu, můžete použít const_cast, což vám nedovolí provádět jiné typy konverzí.
  3. Obsazení jsou ze své podstaty ošklivá - vy jako programátor přepisujete, jak by kompilátor normálně zacházel s vaším kódem. Kompilátoru říkáte: „Já vím lépe než ty.“ V takovém případě je rozumné, že provedení obsazení by mělo být mírně bolestivé, a že by se ve vašem kódu měli vytrvat, protože jsou pravděpodobně zdrojem problémů.

Viz Efektivní C++ Úvod

7
JohnMcG

Je to o tom, kolik typu bezpečnosti chcete uložit.

Když píšete (bar) foo (což je ekvivalentní reinterpret_cast<bar> foo, pokud jste nezadali operátora převodu typu), říkáte kompilátoru, aby ignoroval bezpečnost typu, a prostě udělejte, jak je řečeno.

Když píšete static_cast<bar> foo, žádáte kompilátor, aby alespoň zkontroloval, zda je typová konverze smysluplná, a u integrálních typů vložte nějaký převodový kód.


Úpravy 2014-02-26

Tuto odpověď jsem napsal před více než 5 lety a mýlil jsem se. (Viz komentáře.) Ale stále získává hlasy!

7
Pitarou

static_cast, kromě manipulace s ukazateli na třídy, lze také použít k provádění převodů explicitně definovaných ve třídách ak provádění standardních převodů mezi základními typy:

double d = 3.14159265;
int    i = static_cast<int>(d);
4
prakash

Obsazení ve stylu C lze snadno blokovat v bloku kódu. Obsazení ve stylu C++ není jen lepší praxí; nabízejí mnohem větší stupeň flexibility.

reinterpret_cast umožňuje integrální převod typu ukazatele, může však být v případě zneužití nebezpečný.

static_cast nabízí dobrou konverzi pro číselné typy, např. od výčtů do ints nebo ints do floatů nebo jakýchkoli typů dat, které jste si jistí typem. Neprovádí žádné kontroly doby běhu.

dynamic_cast na druhé straně provede tyto kontroly a označí nejednoznačné přiřazení nebo převody. Pracuje pouze na ukazatelích a odkazech a způsobuje režii.

Existuje několik dalších, ale to jsou hlavní, se kterými se setkáte.

4
Konrad