web-dev-qa-db-de.com

Wann sollten static_cast, dynamic_cast, const_cast und reinterpret_cast verwendet werden?

Was sind die richtigen Verwendungen von:

  • static_cast
  • dynamic_cast
  • const_cast
  • reinterpret_cast
  • C-Style Cast (type)value
  • Funktionsweise Besetzung type(value)

Wie entscheidet man, in welchen speziellen Fällen?

2197
e.James

static_castist der erste Cast, den Sie verwenden sollten. B. implizite Konvertierungen zwischen Typen (z. B. int nach float oder Zeiger auf void*), und es können auch explizite Konvertierungsfunktionen (oder implizite) aufgerufen werden. In vielen Fällen ist die explizite Angabe von static_cast nicht notwendig, aber es ist wichtig zu wissen, dass die T(something)-Syntax (T)something äquivalent ist und vermieden werden sollte (dazu später mehr). Eine T(something, something_else) ist jedoch sicher und ruft den Konstruktor garantiert auf.

static_cast kann auch Vererbungshierarchien durchlaufen. Es ist beim Casting nach oben (in Richtung einer Basisklasse) nicht erforderlich, aber beim Casting nach unten kann es verwendet werden, solange es keine virtual-Vererbung durchführt. Es wird jedoch keine Überprüfung durchgeführt, und es ist undefiniertes Verhalten, eine Hierarchie auf einen Typ zu static_casten, der nicht der Typ des Objekts ist.


const_castkann verwendet werden, um const zu entfernen oder einer Variablen hinzuzufügen; Kein anderer C++ - Cast kann es entfernen (auch nicht reinterpret_cast). Es ist wichtig zu beachten, dass das Ändern eines früheren const-Werts nur undefiniert ist, wenn die ursprüngliche Variable const ist. Wenn Sie es verwenden, um die const von einem Verweis auf etwas zu entfernen, das nicht mit const deklariert wurde, ist es sicher. Dies kann beispielsweise nützlich sein, wenn Memberfunktionen basierend auf const überladen werden. Es kann auch verwendet werden, um einem Objekt const hinzuzufügen, beispielsweise um eine Memberfunktion überladen aufzurufen.

const_cast funktioniert auch bei volatile ähnlich, obwohl dies weniger üblich ist.


dynamic_castwird ausschließlich zum Umgang mit Polymorphismen verwendet. Sie können einen Zeiger oder einen Verweis auf einen beliebigen polymorphen Typ auf einen anderen Klassentyp anwenden (ein polymorpher Typ hat mindestens eine virtuelle Funktion, deklariert oder geerbt). Sie können es nicht nur nach unten werfen, sondern auch seitlich werfen oder sogar eine andere Kette hochlegen. Der dynamic_cast sucht das gewünschte Objekt aus und gibt es wenn möglich zurück. Wenn dies nicht möglich ist, wird im Falle eines Zeigers nullptr zurückgegeben oder im Falle einer Referenz std::bad_cast.

dynamic_cast hat jedoch einige Einschränkungen. Es funktioniert nicht, wenn es mehrere Objekte desselben Typs in der Vererbungshierarchie gibt (den sogenannten "gefürchteten Diamanten") und Sie keine virtual-Vererbung verwenden. Es kann auch nur die öffentliche Vererbung durchlaufen - es wird immer die protected- oder private-Vererbung nicht durchlaufen. Dies ist jedoch selten ein Problem, da solche Formen der Vererbung selten sind.


reinterpret_castist die gefährlichste Besetzung und sollte sehr sparsam eingesetzt werden. Es wandelt einen Typ direkt in einen anderen um, z. B. den Wert von einem Zeiger auf einen anderen umwandeln oder einen Zeiger in einer int oder alle möglichen anderen bösen Dinge speichern. Die einzige Garantie, die Sie mit reinterpret_cast erhalten, besteht im Wesentlichen darin, dass Sie normalerweise den gleichen Wert erhalten, wenn Sie das Ergebnis auf den ursprünglichen Typ zurücksetzen (abernicht), wenn der Zwischentyp kleiner als der Originaltyp ist Art). Es gibt eine Reihe von Konvertierungen, diereinterpret_castebenfalls nicht ausführen kann. Es wird hauptsächlich für besonders seltsame Konvertierungen und Bitmanipulationen verwendet, z. B. zum Umwandeln eines Rohdatenstroms in tatsächliche Daten oder zum Speichern von Daten in den niedrigen Bits eines ausgerichteten Zeigers.


Cast im C-Stil und im Funktionsstil sind Casts mit (type)object bzw. type(object) und sind funktional äquivalent. Sie sind definiert als die erste der folgenden, die erfolgreich ist:

  • const_cast
  • static_cast (obwohl Zugriffsbeschränkungen ignoriert werden)
  • static_cast (siehe oben), dann const_cast
  • reinterpret_cast
  • reinterpret_cast, dann const_cast

Es kann daher in einigen Fällen als Ersatz für andere Besetzungen verwendet werden, kann jedoch extrem gefährlich sein, da es in reinterpret_cast umgewandelt werden kann. Letzteres sollte bevorzugt werden, wenn explizite Besetzung erforderlich ist, es sei denn, Sie sind sich sicher, dass static_cast erfolgreich ist oder nicht reinterpret_cast wird fehlschlagen. Ziehen Sie auch dann die längere, explizite Option in Betracht.

Casts im C-Stil ignorieren auch die Zugriffskontrolle, wenn sie einen static_cast ausführen. Dies bedeutet, dass sie die Möglichkeit haben, eine Operation auszuführen, die keine andere Casts ausführen können. Dies ist jedoch meistens ein Trubel, und in meinen Augen ist es nur ein weiterer Grund, Casts im C-Stil zu vermeiden.

2330
coppro

Verwenden Sie dynamic_cast zum Konvertieren von Zeigern/Referenzen innerhalb einer Vererbungshierarchie.

Verwenden Sie static_cast für gewöhnliche Typkonvertierungen.

Verwenden Sie reinterpret_cast, um Bitmuster neu zu interpretieren. Mit äußerster Vorsicht verwenden.

Verwenden Sie const_cast, um const/volatile wegzuwerfen. Vermeiden Sie dies, wenn Sie nicht mit einer const-wrong API arbeiten.

303
Fred Larson

(Viele theoretische und konzeptionelle Erklärungen wurden oben gegeben.) 

Nachfolgend finden Sie einige praktische Beispiele, wenn ich static_cast, dynamic_cast, const_cast, reinterpret_cast verwendet habe.

(Verweist auch darauf, um die Erklärung zu verstehen: http://www.cplusplus.com/doc/tutorial/typecasting/ )

static_cast:

OnEventData(void* pData)

{
  ......

  //  pData is a void* pData, 

  //  EventData is a structure e.g. 
  //  typedef struct _EventData {
  //  std::string id;
  //  std:: string remote_id;
  //  } EventData;

  // On Some Situation a void pointer *pData
  // has been static_casted as 
  // EventData* pointer 

  EventData *evtdata = static_cast<EventData*>(pData);
  .....
}

dynamic_cast:

void DebugLog::OnMessage(Message *msg)
{
    static DebugMsgData *debug;
    static XYZMsgData *xyz;

    if(debug = dynamic_cast<DebugMsgData*>(msg->pdata)){
        // debug message
    }
    else if(xyz = dynamic_cast<XYZMsgData*>(msg->pdata)){
        // xyz message
    }
    else/* if( ... )*/{
        // ...
    }
}

const_cast:

// *Passwd declared as a const

const unsigned char *Passwd


// on some situation it require to remove its constness

const_cast<unsigned char*>(Passwd)

reinterpret_cast:

typedef unsigned short uint16;

// Read Bytes returns that 2 bytes got read. 

bool ByteBuffer::ReadUInt16(uint16& val) {
  return ReadBytes(reinterpret_cast<char*>(&val), 2);
}
170
Sumit Arora

Es kann hilfreich sein, wenn Sie ein wenig über Interna wissen ...

static_cast

  • C++ - Compiler weiß bereits, wie er Scaler-Typen wie Float in Int konvertiert. Verwenden Sie static_cast für sie.
  • Wenn Sie den Compiler bitten, vom Typ A in B zu konvertieren, ruft static_cast den Konstruktor von B auf, wobei Sie A als Parameter übergeben. Alternativ könnte A einen Konvertierungsoperator (d. H. A::operator B()) haben. Wenn B keinen solchen Konstruktor hat oder A keinen Konvertierungsoperator hat, wird ein Fehler beim Kompilieren angezeigt. 
  • Die Umwandlung von A* in B* ist immer erfolgreich, wenn sich A und B in der Vererbungshierarchie befinden (oder ungültig sind). Andernfalls wird ein Kompilierungsfehler angezeigt.
  • Gotcha : Wenn Sie den Basiszeiger in einen abgeleiteten Zeiger umwandeln, aber wenn das tatsächliche Objekt kein wirklich abgeleiteter Typ ist, erhalten Sie nicht einen Fehler. Sie erhalten einen fehlerhaften Zeiger und höchstwahrscheinlich zur Laufzeit einen Segfault. Gleiches gilt für A& bis B&.
  • Gotcha : Cast von Derived to Base oder viceversa erstellt neue Kopie! Für Leute, die aus C #/Java kommen, kann dies eine große Überraschung sein, da das Ergebnis im Wesentlichen ein abgeschnittenes Objekt ist, das aus Derived erstellt wurde.

dynamischer_cast

  • dynamic_cast verwendet Laufzeitartendaten, um herauszufinden, ob die Umwandlung gültig ist. Beispielsweise kann (Base*) bis (Derived*) fehlschlagen, wenn der Zeiger nicht tatsächlich vom abgeleiteten Typ ist.
  • Das bedeutet, dass dynamic_cast im Vergleich zu static_cast sehr teuer ist!
  • Wenn A* bis B* ungültig ist, gibt dynamic_cast nullptr zurück.
  • Wenn A& bis B& ungültig ist, wirft Dynamic_cast eine Bad_cast-Ausnahme aus.
  • Im Gegensatz zu anderen Besetzungen gibt es einen Laufzeitaufwand.

const_cast

  • Während static_cast non-const zu const ausführen kann, kann es nicht anders gehen. Der const_cast kann auf zwei Arten arbeiten.
  • Ein Beispiel, bei dem dies praktisch ist, ist das Durchlaufen eines Containers wie set<T>, der nur seine Elemente als const zurückgibt, um sicherzustellen, dass der Schlüssel nicht geändert wird. Wenn Sie jedoch beabsichtigen, die Nichtschlüsselelemente eines Objekts zu ändern, sollte dies in Ordnung sein. Sie können const_cast verwenden, um Konstante zu entfernen.
  • Ein anderes Beispiel ist, wenn Sie T& foo() sowie const T& foo() implementieren möchten. Um Code-Duplizierung zu vermeiden, können Sie const_cast anwenden, um den Wert einer Funktion von einer anderen zurückzugeben.

reinterpret_cast

  • Dies bedeutet im Wesentlichen, dass diese Bytes an diesem Speicherort als gegebenes Objekt betrachtet werden.
  • Sie können zum Beispiel 4 Byte Float in 4 Byte Int laden, um zu sehen, wie Bits im Float aussehen.
  • Wenn die Daten für den Typ nicht korrekt sind, kann es zu Abweichungen kommen.
  • Für diesen Cast gibt es keinen Laufzeit-Overhead.
67
Shital Shah

Beantwortet this Ihre Frage?

Ich habe reinterpret_cast noch nie verwendet, und ich frage mich, ob es nicht nach schlechtem Design riecht, wenn es in einen Fall läuft, der es braucht. In der Code-Basis, an der ich arbeite, wird dynamic_cast häufig verwendet. Der Unterschied zu static_cast besteht darin, dass ein dynamic_cast eine Laufzeitüberprüfung durchführt, die (sicherer) oder auch nicht (mehr Aufwand) sein kann (siehe msdn ).

12
andreas buykx

Neben den bisherigen Antworten gibt es hier ein nicht offensichtliches Beispiel, bei dem static_cast nicht ausreicht, um reinterpret_cast zu benötigen. Angenommen, es gibt eine Funktion, die in einem Ausgabeparameter Zeiger auf Objekte verschiedener Klassen (die keine gemeinsame Basisklasse haben) zurückgibt. Ein reales Beispiel für eine solche Funktion ist CoCreateInstance() (siehe den letzten Parameter, der eigentlich void** ist). Angenommen, Sie fordern von dieser Funktion eine bestimmte Objektklasse an, so dass Sie den Typ des Zeigers im Voraus wissen (was Sie häufig für COM-Objekte tun). In diesem Fall können Sie den Zeiger nicht mit void** in static_cast umwandeln: Sie benötigen reinterpret_cast<void**>(&yourPointer)

In Code:

#include <windows.h>
#include <netfw.h>
.....
INetFwPolicy2* pNetFwPolicy2 = nullptr;
HRESULT hr = CoCreateInstance(__uuidof(NetFwPolicy2), nullptr,
    CLSCTX_INPROC_SERVER, __uuidof(INetFwPolicy2),
    //static_cast<void**>(&pNetFwPolicy2) would give a compile error
    reinterpret_cast<void**>(&pNetFwPolicy2) );

static_cast funktioniert jedoch für einfache Zeiger (nicht für Zeiger auf Zeiger), sodass der obige Code umgeschrieben werden kann, um reinterpret_cast (zum Preis einer zusätzlichen Variablen) auf folgende Weise zu vermeiden:

#include <windows.h>
#include <netfw.h>
.....
INetFwPolicy2* pNetFwPolicy2 = nullptr;
void* tmp = nullptr;
HRESULT hr = CoCreateInstance(__uuidof(NetFwPolicy2), nullptr,
    CLSCTX_INPROC_SERVER, __uuidof(INetFwPolicy2),
    &tmp );
pNetFwPolicy2 = static_cast<INetFwPolicy2*>(tmp);
12
Serge Rogatch

Während andere Antworten alle Unterschiede zwischen C++ - Umwandlungen gut beschrieben haben, möchte ich eine kurze Anmerkung hinzufügen, warum Sie keine C-artigen Umwandlungen (Type) var und Type(var) verwenden sollten.

Für C++ - Anfänger wirken C-artige Casts wie die Superset-Operation über C++ - Casts (static_cast <> (), dynamic_cast <> (), const_cast <> (), reinterpret_cast <> ()), und jemand könnte sie den C++ Casts vorziehen . In der Tat ist die Besetzung im C-Stil die Obermenge und kürzer zu schreiben.

Das Hauptproblem bei Casts im C-Stil besteht darin, dass sie die tatsächliche Absicht des Entwicklers des Casts verbergen. Die Casts im C-Stil können praktisch alle Arten von Castings ausführen, von normalerweise sicheren Casts mit static_cast <> () und dynamic_cast <> () bis zu potenziell gefährlichen Casts wie const_cast <> (), wobei der Modifikator const mit den const-Variablen entfernt werden kann kann geändert werden und reinterpret_cast <> () kann sogar ganzzahlige Werte in Zeiger neu interpretieren.

Hier ist die Probe.

int a=Rand(); // Random number.

int* pa1=reinterpret_cast<int*>(a); // OK. Here developer clearly expressed he wanted to do this potentially dangerous operation.

int* pa2=static_cast<int*>(a); // Compiler error.
int* pa3=dynamic_cast<int*>(a); // Compiler error.

int* pa4=(int*) a; // OK. C-style cast can do such cast. The question is if it was intentional or developer just did some typo.

*pa4=5; // Program crashes.

Der Hauptgrund, warum C++ - Casts zur Sprache hinzugefügt wurden, bestand darin, einem Entwickler zu ermöglichen, seine Absichten zu klären - warum er diesen Cast machen wird. Durch die Verwendung von Casts im C-Stil, die in C++ vollkommen gültig sind, wird Ihr Code weniger lesbar und fehleranfälliger, insbesondere für andere Entwickler, die Ihren Code nicht erstellt haben. Um Ihren Code lesbarer und expliziter zu machen, sollten Sie daher C++ - Casts immer den C-Stilen vorziehen.

Hier ist ein kurzes Zitat aus Bjarne Stroustrup (dem Autor von C++), Buch The C++ Programming Language, 4. Auflage - Seite 302.

Diese Besetzung im C-Stil ist weitaus gefährlicher als die genannten Konvertierungsoperatoren weil die Notation in einem großen Programm schwieriger zu erkennen ist und die Art der vom Programmierer beabsichtigten Konvertierung nicht explizit ist.

6
Timmy_A

Um dies zu verstehen, lassen Sie uns das folgende Codefragment betrachten:

struct Foo{};
struct Bar{};

int main(int argc, char** argv)
{
    Foo* f = new Foo;

    Bar* b1 = f;                              // (1)
    Bar* b2 = static_cast<Bar*>(f);           // (2)
    Bar* b3 = dynamic_cast<Bar*>(f);          // (3)
    Bar* b4 = reinterpret_cast<Bar*>(f);      // (4)
    Bar* b5 = const_cast<Bar*>(f);            // (5)

    return 0;
}

Nur Zeile (4) wird ohne Fehler kompiliert. Nur reinterpret_cast kann verwendet werden, um einen Zeiger auf ein Objekt in einen Zeiger auf einen beliebigen nicht verknüpften Objekttyp umzuwandeln.

Zu beachten ist Folgendes: Das dynamic_cast schlägt zur Laufzeit fehl, wird jedoch bei den meisten Compilern nicht kompiliert, da in der Struktur des gegossenen Zeigers keine virtuellen Funktionen vorhanden sind, dh dynamic_cast funktioniert nur mit polymorphen Klassenzeigern.

Wann C++ - Cast verwenden :

  • Verwenden Sie static_cast als Entsprechung einer Umwandlung im C-Stil, die eine Wertkonvertierung durchführt, oder wenn Sie einen Zeiger explizit von einer Klasse in ihre Oberklasse konvertieren müssen.
  • Verwenden Sie const_cast , um das const-Qualifikationsmerkmal zu entfernen. 
  • Verwenden Sie reinterpret_cast , um unsichere Konvertierungen von Zeigertypen in und aus Ganzzahlen und anderen Zeigertypen durchzuführen. Verwenden Sie dies nur, wenn wir wissen, was wir tun und die Aliasing-Probleme verstehen.
0