Was sind die richtigen Verwendungen von:
static_cast
dynamic_cast
const_cast
reinterpret_cast
(type)value
type(value)
Wie entscheidet man, in welchen speziellen Fällen?
static_cast
ist 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_cast
en, der nicht der Typ des Objekts ist.
const_cast
kann 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_cast
wird 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_cast
ist 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_cast
ebenfalls 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.
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.
(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);
}
Es kann hilfreich sein, wenn Sie ein wenig über Interna wissen ...
static_cast
static_cast
für sie.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. A*
in B*
ist immer erfolgreich, wenn sich A und B in der Vererbungshierarchie befinden (oder ungültig sind). Andernfalls wird ein Kompilierungsfehler angezeigt.A&
bis B&
.dynamischer_cast
(Base*)
bis (Derived*)
fehlschlagen, wenn der Zeiger nicht tatsächlich vom abgeleiteten Typ ist.A*
bis B*
ungültig ist, gibt dynamic_cast nullptr zurück.A&
bis B&
ungültig ist, wirft Dynamic_cast eine Bad_cast-Ausnahme aus.const_cast
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.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
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 ).
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);
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.
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 :