Zoeken…


Invoering

Een uitdrukking kan expliciet worden geconverteerd of cast naar type T met behulp van dynamic_cast<T> , static_cast<T> , reinterpret_cast<T> of const_cast<T> , afhankelijk van het type cast dat bedoeld is.

C ++ ondersteunt ook castnotatie in functiestijl, T(expr) en T(expr) C-stijl, (T)expr .

Syntaxis

  • simple-type-specifier ( )
  • simple-type-specifier ( expressie-lijst )
  • simple-type-specificatie schrap-init-lijst
  • typenaam-specificatie ( )
  • typename-specifier ( expression-list )
  • typenaam-specificatie schrap-init-lijst
  • dynamic_cast < type-id > ( expressie )
  • static_cast < type-id > ( expressie )
  • reinterpret_cast < type-id > ( expressie )
  • const_cast < type-id > ( expressie )
  • ( type-id ) cast-expressie

Opmerkingen

Alle zes castnotaties hebben één ding gemeen:

  • Casten naar een lvalue-referentietype, zoals in dynamic_cast<Derived&>(base) , levert een lvalue op. Daarom, als u iets met hetzelfde object wilt doen maar het als een ander type wilt behandelen, zou u naar een lvalue-referentietype casten.
  • Casten naar een referentietype waarde, zoals in static_cast<string&&>(s) , levert een waarde op.
  • Casten naar een niet-referentietype, zoals in (int)x , levert een prvalue op, die kan worden beschouwd als een kopie van de te casten waarde, maar met een ander type dan het origineel.

Het sleutelwoord reinterpret_cast is verantwoordelijk voor het uitvoeren van twee verschillende soorten "onveilige" conversies:

Het trefwoord static_cast kan verschillende conversies uitvoeren:

  • Basis naar afgeleide conversies

  • Elke conversie die kan worden uitgevoerd door een directe initialisatie, inclusief zowel impliciete conversies als conversies die een expliciete constructor of conversiefunctie oproepen. Zie hier en hier voor meer informatie.

  • void , waardoor de waarde van de uitdrukking wordt genegeerd.

    // on some compilers, suppresses warning about x being unused
    static_cast<void>(x);
    
  • Tussen rekenkundige en opsommingstypes en tussen verschillende opsommingstypes. Zie enum-conversies

  • Van wijzer tot lid van afgeleide klasse, tot wijzer tot lid van basisklasse. De typen waarnaar moet worden verwezen, moeten overeenkomen. Zie afgeleid naar basisconversie voor verwijzingen naar leden

  • void* tot T* .

C ++ 11

Basis naar afgeleide conversie

Een pointer naar basisklasse kan worden omgezet in een pointer naar afgeleide klasse met behulp van static_cast . static_cast voert geen runtime-controle uit en kan leiden tot ongedefinieerd gedrag wanneer de aanwijzer niet naar het gewenste type verwijst.

struct Base {};
struct Derived : Base {};
Derived d;
Base* p1 = &d;
Derived* p2 = p1;                        // error; cast required
Derived* p3 = static_cast<Derived*>(p1); // OK; p2 now points to Derived object
Base b;
Base* p4 = &b;
Derived* p5 = static_cast<Derived*>(p4); // undefined behaviour since p4 does not
                                         // point to a Derived object

Evenzo kan een verwijzing naar basisklasse worden omgezet in een verwijzing naar afgeleide klasse met behulp van static_cast .

struct Base {};
struct Derived : Base {};
Derived d;
Base& r1 = d;
Derived& r2 = r1;                        // error; cast required
Derived& r3 = static_cast<Derived&>(r1); // OK; r3 now refers to Derived object

Als het dynamic_cast polymorf is, kan dynamic_cast worden gebruikt om een conversie van base naar afgeleide uit te voeren. Het voert een runtime-controle uit en de fout kan worden hersteld in plaats van ongedefinieerd gedrag te veroorzaken. In het geval van de aanwijzer wordt een nulaanwijzer geretourneerd bij mislukking. In het referentiegeval wordt een uitzondering gegenereerd bij het falen van het type std::bad_cast (of een klasse afgeleid van std::bad_cast ).

struct Base { virtual ~Base(); }; // Base is polymorphic
struct Derived : Base {};
Base* b1 = new Derived;
Derived* d1 = dynamic_cast<Derived*>(b1); // OK; d1 points to Derived object
Base* b2 = new Base;
Derived* d2 = dynamic_cast<Derived*>(b2); // d2 is a null pointer

Constness weggooien

Een pointer naar een const-object kan worden geconverteerd naar een pointer naar een niet-const-object met behulp van het sleutelwoord const_cast . Hier gebruiken we const_cast om een functie aan te roepen die niet const-correct is. Het accepteert alleen een niet-const char* argument, hoewel het nooit door de aanwijzer schrijft:

void bad_strlen(char*);
const char* s = "hello, world!";
bad_strlen(s);                    // compile error
bad_strlen(const_cast<char*>(s)); // OK, but it's better to make bad_strlen accept const char*

const_cast naar referentietype kan worden gebruikt om een const-gekwalificeerde waarde om te zetten in een niet-const-gekwalificeerde waarde.

const_cast is gevaarlijk omdat het het C ++ -type systeem onmogelijk maakt om te voorkomen dat u probeert een const-object te wijzigen. Dit resulteert in ongedefinieerd gedrag.

const int x = 123;
int& mutable_x = const_cast<int&>(x);
mutable_x = 456; // may compile, but produces *undefined behavior*

Typ punning-conversie

Een pointer (resp. Referentie) naar een objecttype kan worden geconverteerd naar een pointer (resp. Referentie) naar een ander objecttype met behulp van reinterpret_cast . Dit roept geen constructors of conversiefuncties op.

int x = 42;
char* p = static_cast<char*>(&x);      // error: static_cast cannot perform this conversion
char* p = reinterpret_cast<char*>(&x); // OK
*p = 'z';                              // maybe this modifies x (see below)
C ++ 11

Het resultaat van reinterpret_cast vertegenwoordigt hetzelfde adres als de operand, op voorwaarde dat het adres correct is uitgelijnd voor het type bestemming. Anders is het resultaat niet gespecificeerd.

int x = 42;
char& r = reinterpret_cast<char&>(x);
const void* px = &x;
const void* pr = &r;
assert(px == pr); // should never fire
C ++ 11

Het resultaat van reinterpret_cast is niet gespecificeerd, behalve dat een pointer (resp. Referentie) een retour van het brontype naar het bestemmingstype en terug zal overleven, zolang de uitlijningsvereiste van het bestemmingstype niet strenger is dan die van het brontype.

int x = 123;
unsigned int& r1 = reinterpret_cast<unsigned int&>(x);
int& r2 = reinterpret_cast<int&>(r1);
r2 = 456; // sets x to 456

Bij de meeste implementaties verandert reinterpret_cast het adres niet, maar deze vereiste was pas gestandaardiseerd tot C ++ 11.

reinterpret_cast kan ook worden gebruikt om van het ene pointer-naar-data-lidtype naar het andere te converteren, of het ene pointer-naar-lid-functietype naar een ander.

Het gebruik van reinterpret_cast wordt als gevaarlijk beschouwd, omdat lezen of schrijven via een aanwijzer of verwijzing verkregen met reinterpret_cast ongedefinieerd gedrag kan veroorzaken wanneer de bron- en doeltypen niet aan elkaar gerelateerd zijn.

Conversie tussen aanwijzer en geheel getal

Een object pointer (inclusief void* ) of functie pointer kan worden geconverteerd naar een geheel getal met behulp van reinterpret_cast . Dit compileert alleen als het type bestemming lang genoeg is. Het resultaat is door de implementatie bepaald en levert meestal het numerieke adres op van de byte in het geheugen waarnaar de aanwijzer verwijst.

Meestal is long of unsigned long genoeg om een pointerwaarde te bevatten, maar dit wordt niet gegarandeerd door de standaard.

C ++ 11

Als de typen std::intptr_t en std::uintptr_t bestaan, zijn ze gegarandeerd lang genoeg om een void* (en dus elke aanwijzer naar het objecttype). Ze zijn echter niet gegarandeerd lang genoeg om een functie-aanwijzer te bevatten.

Evenzo kan reinterpret_cast worden gebruikt om een geheel getal om te zetten in een pointertype. Wederom is het resultaat door de implementatie bepaald, maar een pointerwaarde is gegarandeerd onveranderd door een round trip door een geheel getal. De standaard garandeert niet dat de waarde nul wordt omgezet in een nulwijzer.

void register_callback(void (*fp)(void*), void* arg); // probably a C API
void my_callback(void* x) {
    std::cout << "the value is: " << reinterpret_cast<long>(x); // will probably compile
}
long x;
std::cin >> x;
register_callback(my_callback,
                  reinterpret_cast<void*>(x)); // hopefully this doesn't lose information...

Conversie door expliciete constructor of expliciete conversiefunctie

Een conversie waarbij een expliciete constructor of conversiefunctie wordt aangeroepen, kan niet impliciet worden uitgevoerd. We kunnen vragen dat de conversie expliciet wordt uitgevoerd met behulp van static_cast . De betekenis is hetzelfde als die van een directe initialisatie, behalve dat het resultaat tijdelijk is.

class C {
    std::unique_ptr<int> p;
  public:
    explicit C(int* p) : p(p) {}
};
void f(C c);
void g(int* p) {
    f(p);                 // error: C::C(int*) is explicit
    f(static_cast<C>(p)); // ok
    f(C(p));              // equivalent to previous line
    C c(p); f(c);         // error: C is not copyable
}

Impliciete conversie

static_cast kan elke impliciete conversie uitvoeren. Dit gebruik van static_cast kan soms nuttig zijn, zoals in de volgende voorbeelden:

  • Bij het doorgeven van argumenten aan een weglatingsteken is het "verwachte" argumenttype niet statisch bekend, dus er vindt geen impliciete conversie plaats.

    const double x = 3.14;
    printf("%d\n", static_cast<int>(x)); // prints 3
    // printf("%d\n", x); // undefined behaviour; printf is expecting an int here
    // alternative:
    // const int y = x; printf("%d\n", y);
    

    Zonder de expliciete typeconversie zou een double object aan de ellips worden doorgegeven en zou ongedefinieerd gedrag optreden.

  • Een afgeleide klasse-toewijzingsoperator kan een basisklasse-toewijzingsoperator als volgt aanroepen:

    struct Base { /* ... */ };
    struct Derived : Base {
        Derived& operator=(const Derived& other) {
            static_cast<Base&>(*this) = other;
            // alternative:
            // Base& this_base_ref = *this; this_base_ref = other;
        }
    };
    

Aantal conversies

static_cast kan van een geheel of drijvend static_cast naar een opsommingstype (scoped of unscoped) converteren, en vice versa. Het kan ook converteren tussen opsommingstypes.

  • De conversie van een type zonder scènes naar een rekenkundig type is een impliciete conversie; het is mogelijk, maar niet noodzakelijk, om static_cast te gebruiken.
C ++ 11
  • Wanneer een scoped-opsommingstype wordt omgezet in een rekenkundig type:

    • Als de waarde van het getal exact in het doeltype kan worden weergegeven, is het resultaat die waarde.
    • Anders is het resultaat niet gespecificeerd als het type bestemming een geheel getal is.
    • Anders is het resultaat hetzelfde als dat van het converteren naar het onderliggende type en vervolgens naar het drijvende-komma-type als het doeltype een drijvend punttype is.

    Voorbeeld:

    enum class Format {
        TEXT = 0,
        PDF = 1000,
        OTHER = 2000,
    };
    Format f = Format::PDF;
    int a = f;                         // error
    int b = static_cast<int>(f);       // ok; b is 1000
    char c = static_cast<char>(f);     // unspecified, if 1000 doesn't fit into char
    double d = static_cast<double>(f); // d is 1000.0... probably
    
  • Wanneer een geheel getal of opsommingstype wordt geconverteerd naar een opsommingstype:

    • Als de oorspronkelijke waarde binnen het bereik van de doelgroep ligt, is het resultaat die waarde. Merk op dat deze waarde mogelijk ongelijk is voor alle opsommers.
    • Anders is het resultaat niet gespecificeerd (<= C ++ 14) of niet gedefinieerd (> = C ++ 17).

    Voorbeeld:

    enum Scale {
        SINGLE = 1,
        DOUBLE = 2,
        QUAD = 4
    };
    Scale s1 = 1;                     // error
    Scale s2 = static_cast<Scale>(2); // s2 is DOUBLE
    Scale s3 = static_cast<Scale>(3); // s3 has value 3, and is not equal to any enumerator
    Scale s9 = static_cast<Scale>(9); // unspecified value in C++14; UB in C++17
    
C ++ 11
  • Wanneer een drijvend punttype wordt geconverteerd naar een opsommingstype, is het resultaat hetzelfde als het omzetten naar het onderliggende type van het opsommingstype en vervolgens naar het opsommingstype.

    enum Direction {
        UP = 0,
        LEFT = 1,
        DOWN = 2,
        RIGHT = 3,
    };
    Direction d = static_cast<Direction>(3.14); // d is RIGHT
    

Afgeleid naar basisconversie voor verwijzingen naar leden

Een pointer naar lid van afgeleide klasse kan worden omgezet in een pointer naar lid van basisklasse met behulp van static_cast . De typen waarnaar moet worden verwezen, moeten overeenkomen.

Als de operand een nul-aanwijzer naar lidwaarde is, is het resultaat ook een nul-aanwijzer naar lidwaarde.

Anders is de conversie alleen geldig als het lid waarnaar de operand verwijst in werkelijkheid in de doelklasse bestaat, of als de doelklasse een basis- of afgeleide klasse is van de klasse met het lid waarnaar de operand verwijst. static_cast controleert niet op geldigheid. Als de conversie niet geldig is, is het gedrag niet gedefinieerd.

struct A {};
struct B { int x; };
struct C : A, B { int y; double z; };
int B::*p1 = &B::x;
int C::*p2 = p1;                              // ok; implicit conversion
int B::*p3 = p2;                              // error
int B::*p4 = static_cast<int B::*>(p2);       // ok; p4 is equal to p1
int A::*p5 = static_cast<int A::*>(p2);       // undefined; p2 points to x, which is a member
                                              // of the unrelated class B
double C::*p6 = &C::z;
double A::*p7 = static_cast<double A::*>(p6); // ok, even though A doesn't contain z
int A::*p8 = static_cast<int A::*>(p6);       // error: types don't match

ongeldig * tot T *

In C ++ kan void* niet impliciet worden geconverteerd naar T* waarbij T een objecttype is. In plaats daarvan moet static_cast worden gebruikt om de conversie expliciet uit te voeren. Als de operand daadwerkelijk naar een T object verwijst, verwijst het resultaat naar dat object. Anders is het resultaat niet gespecificeerd.

C ++ 11

Zelfs als de operand niet naar een T object verwijst, zolang de operand verwijst naar een byte waarvan het adres correct is uitgelijnd voor het type T , wijst het resultaat van de conversie naar dezelfde byte.

// allocating an array of 100 ints, the hard way
int* a = malloc(100*sizeof(*a));                    // error; malloc returns void*
int* a = static_cast<int*>(malloc(100*sizeof(*a))); // ok
// int* a = new int[100];                           // no cast needed
// std::vector<int> a(100);                         // better

const char c = '!';
const void* p1 = &c;
const char* p2 = p1;                           // error
const char* p3 = static_cast<const char*>(p1); // ok; p3 points to c
const int* p4 = static_cast<const int*>(p1);   // unspecified in C++03;
                                               // possibly unspecified in C++11 if
                                               // alignof(int) > alignof(char)
char* p5 = static_cast<char*>(p1);             // error: casting away constness

C-stijl casting

C-Style casting kan worden beschouwd als 'Best effort' casting en wordt zo genoemd omdat het de enige cast is die in C zou kunnen worden gebruikt. De syntaxis voor deze cast is (NewType)variable .

Wanneer deze cast wordt gebruikt, gebruikt deze een van de volgende c ++ casts (in volgorde):

  • const_cast<NewType>(variable)
  • static_cast<NewType>(variable)
  • const_cast<NewType>(static_cast<const NewType>(variable))
  • reinterpret_cast<const NewType>(variable)
  • const_cast<NewType>(reinterpret_cast<const NewType>(variable))

Functionele casting lijkt erg op elkaar, maar als een paar beperkingen als gevolg van de syntaxis: NewType(expression) . Hierdoor kunnen alleen typen zonder spaties worden gecast.

Het is beter om de nieuwe c ++ cast te gebruiken, omdat deze beter leesbaar is en overal in een C ++ broncode kan worden gezien en fouten tijdens het compileren worden gedetecteerd, in plaats van tijdens het uitvoeren.

Omdat deze cast kan leiden tot onbedoelde reinterpret_cast , wordt het vaak als gevaarlijk beschouwd.



Modified text is an extract of the original Stack Overflow Documentation
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow