Suche…


Einführung

Ein Ausdruck kann explizit umgewandelt oder gegossen werden auf den Typ T mit dynamic_cast<T> , static_cast<T> , reinterpret_cast<T> oder const_cast<T> , je nachdem , welche Art von Guss soll.

C ++ unterstützt auch Cast-Notation im Funktionsstil, T(expr) und Cast-Notation im C-Stil (T)expr .

Syntax

  • einfacher Typbezeichner ( )
  • einfacher Typbezeichner ( Ausdrucksliste )
  • Einfacher Typbezeichner Braced-Init-List
  • Typname-Bezeichner ( )
  • Typname-Bezeichner ( Ausdrucksliste )
  • typename-spezifizierer braced -init-list
  • dynamic_cast < Typ-ID > ( Ausdruck )
  • static_cast < Typ-ID > ( Ausdruck )
  • reinterpret_cast < Typ-ID > ( Ausdruck )
  • const_cast < Typ-ID > ( Ausdruck )
  • ( Typ-ID ) Cast-Ausdruck

Bemerkungen

Alle sechs Cast-Notationen haben eines gemeinsam:

  • Die Umwandlung in einen lvalue-Referenztyp, wie in dynamic_cast<Derived&>(base) , ergibt einen lvalue. Wenn Sie also mit demselben Objekt etwas tun, es aber als einen anderen Typ behandeln möchten, würden Sie es in einen Lvalue-Referenztyp umwandeln.
  • Die Umwandlung in einen rvalue-Referenztyp, wie in static_cast<string&&>(s) , ergibt einen rvalue.
  • Die Umwandlung in einen Nichtreferenztyp wie in (int)x ergibt einen pr-Wert, der als Kopie des gegossenen Werts betrachtet werden kann, jedoch mit einem anderen Typ als dem Original.

Das reinterpret_cast Schlüsselwort ist dafür verantwortlich, zwei verschiedene Arten von "unsicheren" Konvertierungen durchzuführen:

Mit static_cast Schlüsselwort static_cast können verschiedene Konvertierungen durchgeführt werden:

  • Basis für abgeleitete Konvertierungen

  • Jede Konvertierung, die durch direkte Initialisierung durchgeführt werden kann, einschließlich impliziter Konvertierungen und Konvertierungen, die einen expliziten Konstruktor oder eine Konvertierungsfunktion aufrufen. Siehe hier und hier für weitere Details.

  • void , wodurch der Wert des Ausdrucks verworfen wird.

    // on some compilers, suppresses warning about x being unused
    static_cast<void>(x);
    
  • Zwischen arithmetischen und Aufzählungstypen und zwischen verschiedenen Aufzählungstypen. Siehe Enum-Konvertierungen

  • Vom Zeiger zum Mitglied der abgeleiteten Klasse, zum Zeiger auf das Mitglied der Basisklasse. Die Typen, auf die gezeigt wird, müssen übereinstimmen. Siehe abgeleitete Basiskonvertierung für Zeiger auf Member

  • void* bis T* .

C ++ 11

Basis für abgeleitete Konvertierung

Ein Zeiger auf die Basisklasse kann mithilfe von static_cast in einen Zeiger auf eine abgeleitete Klasse static_cast . static_cast führt keine Laufzeitüberprüfung durch und kann zu undefiniertem Verhalten führen, wenn der Zeiger tatsächlich nicht auf den gewünschten Typ zeigt.

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

Ebenso kann eine Referenz auf die Basisklasse mit static_cast in eine Referenz auf eine abgeleitete Klasse 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

Wenn der dynamic_cast polymorph ist, kann mit dynamic_cast eine Konvertierung von Basis in abgeleitete dynamic_cast werden. Es führt eine Laufzeitprüfung durch und der Fehler kann behoben werden, anstatt undefiniertes Verhalten zu erzeugen. Im Zeigerfall wird bei einem Fehler ein Nullzeiger zurückgegeben. Im Referenzfall wird bei einem Fehler des Typs std::bad_cast (oder einer von std::bad_cast abgeleiteten Klasse) eine Ausnahme ausgelöst.

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

Konstanz wegwerfen

Ein Zeiger auf ein const-Objekt kann mit dem Schlüsselwort const_cast in einen Zeiger auf ein Nicht-const-Objekt const_cast . Hier rufen wir const_cast auf, um eine Funktion aufzurufen, die nicht const-correct ist. Es akzeptiert nur ein nicht-konstantes char* -Argument, obwohl es niemals durch den Zeiger schreibt:

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 in reference type kann ein const-qualifizierter l-Wert in einen nicht-const-qualifizierten Wert umgewandelt werden.

const_cast ist gefährlich, da das C ++ - Typsystem Sie nicht daran hindern kann, ein const-Objekt zu ändern. Dies führt zu undefiniertem Verhalten.

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

Geben Sie Punning-Konvertierung ein

Ein Zeiger (bzw. eine Referenz) auf einen Objekttyp kann mit reinterpret_cast in einen Zeiger (bzw. eine Referenz) auf einen anderen Objekttyp konvertiert werden. Dies ruft keine Konstruktoren oder Konvertierungsfunktionen auf.

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

Das Ergebnis von reinterpret_cast repräsentiert dieselbe Adresse wie der Operand, vorausgesetzt, die Adresse ist entsprechend dem Zieltyp ausgerichtet. Andernfalls ist das Ergebnis nicht spezifiziert.

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

Das Ergebnis von reinterpret_cast ist nicht spezifiziert, mit der Ausnahme, dass ein Zeiger (bzw. eine Referenz) eine Rundreise vom Quelltyp zum Zieltyp und zurück überlebt, sofern die Ausrichtungsanforderung des Zieltyps nicht strenger ist als die des Quelltyps.

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

Bei den meisten Implementierungen ändert reinterpret_cast die Adresse nicht, diese Anforderung wurde jedoch erst in C ++ 11 standardisiert.

reinterpret_cast kann auch verwendet werden, um einen Zeiger-zu-Daten-Mitgliedstyp in einen anderen oder einen Zeiger-zu-Mitglied-Funktionstyp in einen anderen umzuwandeln.

Die Verwendung von reinterpret_cast wird als gefährlich angesehen, da das Lesen oder Schreiben über einen Zeiger oder eine mit reinterpret_cast erhaltene Referenz ein undefiniertes Verhalten auslösen kann, wenn die Quell- und Zieltypen nicht miteinander zusammenhängen.

Konvertierung zwischen Zeiger und Ganzzahl

Ein Objektzeiger (einschließlich void* ) oder Funktionszeiger kann mit reinterpret_cast in einen Integer-Typ konvertiert werden. Dies wird nur kompiliert, wenn der Zieltyp lang genug ist. Das Ergebnis ist implementierungsdefiniert und liefert normalerweise die numerische Adresse des Bytes im Speicher, auf das der Zeiger zeigt.

In der Regel ist long oder unsigned long lang genug, um einen Zeigerwert aufzunehmen, dies wird jedoch vom Standard nicht garantiert.

C ++ 11

Wenn die Typen std::intptr_t und std::uintptr_t vorhanden sind, sind sie garantiert lang genug, um ein void* (und somit einen Zeiger auf den Objekttyp) aufzunehmen. Sie sind jedoch nicht garantiert lang genug, um einen Funktionszeiger zu halten.

In ähnlicher Weise kann mit reinterpret_cast ein Integer-Typ in einen Zeigertyp konvertiert werden. Das Ergebnis ist wiederum implementierungsdefiniert, jedoch wird garantiert, dass ein Zeigerwert durch eine Rundreise durch einen Integer-Typ unverändert bleibt. Der Standard garantiert nicht, dass der Wert Null in einen Nullzeiger umgewandelt wird.

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...

Konvertierung durch expliziten Konstruktor oder explizite Konvertierungsfunktion

Eine Konvertierung, die den Aufruf eines expliziten Konstruktors oder einer Konvertierungsfunktion beinhaltet, kann nicht implizit durchgeführt werden. Wir können die Konvertierung explizit mit static_cast . Die Bedeutung ist dieselbe wie bei einer direkten Initialisierung, außer dass das Ergebnis temporär ist.

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
}

Implizite Konvertierung

static_cast kann jede implizite Konvertierung durchführen. Diese Verwendung von static_cast kann gelegentlich nützlich sein, wie in den folgenden Beispielen:

  • Bei der Übergabe von Argumenten an eine Ellipse ist der Argumenttyp "erwartet" nicht statisch bekannt, sodass keine implizite Konvertierung erfolgt.

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

    Ohne die explizite Typkonvertierung würde ein double Objekt an die Ellipse übergeben und ein undefiniertes Verhalten würde auftreten.

  • Ein abgeleiteter Klassenzuweisungsoperator kann einen Basisklassenzuweisungsoperator wie folgt aufrufen:

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

Aufzählungsumwandlungen

static_cast kann von einem Integer- oder Fließkommatyp in einen Aufzählungstyp konvertieren (egal ob mit oder ohne Bereich) und umgekehrt. Es kann auch zwischen Aufzählungstypen konvertiert werden.

  • Die Konvertierung von einem nicht begrenzten Aufzählungstyp in einen arithmetischen Typ ist eine implizite Konvertierung. Es ist möglich, aber nicht notwendig, static_cast zu verwenden.
C ++ 11
  • Wenn ein Aufzählungstyp für Bereiche in einen arithmetischen Typ konvertiert wird:

    • Wenn der Wert der Enumeration genau im Zieltyp dargestellt werden kann, ist das Ergebnis dieser Wert.
    • Wenn der Zieltyp ein Integer-Typ ist, ist das Ergebnis sonst nicht angegeben.
    • Wenn der Zieltyp ein Gleitkommatyp ist, ist das Ergebnis dasselbe wie das Konvertieren in den zugrunde liegenden Typ und dann in den Gleitkommatyp.

    Beispiel:

    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
    
  • Wenn eine Ganzzahl oder ein Aufzählungstyp in einen Aufzählungstyp konvertiert wird:

    • Wenn der ursprüngliche Wert innerhalb des Zielbereichs liegt, ist das Ergebnis dieser Wert. Beachten Sie, dass dieser Wert möglicherweise nicht allen Enumeratoren entspricht.
    • Andernfalls ist das Ergebnis nicht spezifiziert (<= C ++ 14) oder undefined (> = C ++ 17).

    Beispiel:

    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
  • Wenn ein Gleitkommatyp in einen Aufzählungstyp konvertiert wird, entspricht das Ergebnis dem Konvertieren in den zugrunde liegenden Typ der Enumeration und dann in den Aufzählungstyp.

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

Abgeleitet in Basisumwandlung für Zeiger auf Mitglieder

Ein Zeiger auf ein Mitglied der abgeleiteten Klasse kann mit static_cast in einen Zeiger auf ein Mitglied der Basisklasse static_cast . Die Typen, auf die gezeigt wird, müssen übereinstimmen.

Wenn der Operand ein Nullzeiger auf den Elementwert ist, ist das Ergebnis auch ein Nullzeiger auf den Elementwert.

Andernfalls ist die Konvertierung nur gültig, wenn der Member, auf den der Operand zeigt, tatsächlich in der Zielklasse vorhanden ist oder wenn die Zielklasse eine Basis- oder abgeleitete Klasse der Klasse ist, die den Member enthält, auf den der Operand zeigt. static_cast nicht auf Gültigkeit. Wenn die Konvertierung nicht gültig ist, ist das Verhalten undefiniert.

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

ungültig * bis T *

In C ++ kann void* nicht implizit in T* konvertiert werden, wobei T ein Objekttyp ist. Stattdessen sollte static_cast verwendet werden, um die Konvertierung explizit durchzuführen. Wenn der Operand tatsächlich auf ein T Objekt zeigt, zeigt das Ergebnis auf dieses Objekt. Andernfalls ist das Ergebnis nicht spezifiziert.

C ++ 11

Selbst wenn der Operand nicht auf ein T Objekt zeigt, solange der Operand auf ein Byte zeigt, dessen Adresse für den Typ T richtig ausgerichtet ist, zeigt das Ergebnis der Konvertierung auf dasselbe 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-Style Casting

C-Style-Casting kann als "Best Effort" -Casting bezeichnet werden und wird so benannt, dass es das einzige Cast ist, das in C verwendet werden (NewType)variable .

Immer wenn dieser Cast verwendet wird, verwendet er einen der folgenden C ++ - Casts (in Reihenfolge):

  • 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))

Das funktionale Casting ist sehr ähnlich, jedoch mit einigen Einschränkungen aufgrund seiner Syntax: NewType(expression) . Daher können nur Typen ohne Leerzeichen umgewandelt werden.

Es ist besser, eine neue C ++ - Konvertierung zu verwenden, da sie besser lesbar ist und leicht an einem beliebigen Ort innerhalb eines C ++ - Quellcodes erkannt werden kann. Fehler werden zur Kompilierzeit statt zur Laufzeit erkannt.

Da diese Umwandlung zu einem unbeabsichtigten reinterpret_cast , wird dies häufig als gefährlich betrachtet.



Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow