Frage 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-Besetzung (type)value
  • Funktionsstil type(value)

Wie entscheidet man, in welchen spezifischen Fällen zu verwenden?


2048
2017-12-01 20:11


Ursprung


Antworten:


static_cast ist die erste Besetzung, die du benutzen solltest. Es macht Dinge wie implizite Konvertierungen zwischen Typen (wie int zu floatoder Zeiger auf void*), und es kann auch explizite Konvertierungsfunktionen (oder implizite) aufrufen. In vielen Fällen explizit angeben static_cast ist nicht notwendig, aber es ist wichtig zu beachten, dass die T(something) Syntax ist äquivalent zu (T)something und sollte vermieden werden (mehr dazu später). EIN T(something, something_else) ist jedoch sicher und garantiert den Konstruktor aufzurufen.

static_cast kann auch Vererbungshierarchien durchlaufen. Es ist unnötig, nach oben zu werfen (in Richtung einer Basisklasse), aber beim Abwärtsgießen kann es verwendet werden, solange es nicht durchschlägt virtual Erbe. Es überprüft jedoch nicht, und es ist undefiniertes Verhalten zu static_cast eine Hierarchie zu einem Typ, der nicht der Typ des Objekts ist.


const_cast kann zum Entfernen oder Hinzufügen verwendet werden const zu einer Variablen; Keine andere C ++ - Umwandlung kann sie entfernen (nicht einmal reinterpret_cast). Es ist wichtig zu beachten, dass das Ändern eines früheren const Wert ist nur undefiniert, wenn die ursprüngliche Variable ist const; wenn du es benutzt, um das zu nehmen const aus einem Verweis auf etwas, das nicht mit deklariert wurde const, es ist sicher. Dies kann nützlich sein, wenn Sie Memberfunktionen auf Basis von überladen const, zum Beispiel. Es kann auch zum Hinzufügen verwendet werden const zu einem Objekt, z. B. um eine Memberfunktionsüberladung aufzurufen.

const_cast funktioniert auch ähnlich auf volatileobwohl das weniger üblich ist.


dynamic_cast wird fast ausschließlich zur Handhabung von Polymorphie verwendet. Sie können einen Zeiger oder 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 damit mehr als nur nach unten werfen - Sie können seitlich oder sogar eine andere Kette werfen. Das dynamic_cast sucht das gewünschte Objekt und gibt es nach Möglichkeit zurück. Wenn es nicht möglich ist, wird es zurückkehren nullptr im Falle eines Zeigers oder werfen std::bad_cast im Falle einer Referenz.

dynamic_cast hat jedoch einige Einschränkungen. Es funktioniert nicht, wenn mehrere Objekte des gleichen Typs in der Vererbungshierarchie (der sogenannte "gefürchtete Diamant") vorhanden sind und Sie nicht verwenden virtual Erbe. Es kann auch nur durch öffentliche Vererbung gehen - es wird immer nicht durchkommen protected oder private Erbe. 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 - beispielsweise den Wert von einem Zeiger auf einen anderen umwandeln oder einen Zeiger in einem anderen speichern intoder alle möglichen anderen unangenehmen Dinge. Hauptsächlich, die einzige Garantie, die Sie bekommen reinterpret_cast Ist das normalerweise, wenn Sie das Ergebnis zum ursprünglichen Typ zurückwerfen, erhalten Sie den genau gleichen Wert (aber nicht wenn der Zwischentyp kleiner als der ursprüngliche Typ ist. Es gibt eine Reihe von Konvertierungen, die reinterpret_cast kann auch nicht. Es wird hauptsächlich für besonders seltsame Konvertierungen und Bitmanipulationen verwendet, wie zum Beispiel das Umwandeln eines Rohdatenstroms in tatsächliche Daten oder das Speichern von Daten in den niedrigen Bits eines ausgerichteten Zeigers.


C-Style-Besetzung und Funktionsstil sind Casts mit (type)object oder type(object), beziehungsweise. Ein C-Style-Cast ist definiert als der erste der folgenden, der erfolgreich ist:

  • const_cast
  • static_cast (obwohl Zugangsbeschränkungen ignoriert werden)
  • static_cast (so), dann const_cast
  • reinterpret_cast
  • reinterpret_cast, dann const_cast

Es kann daher in einigen Fällen als Ersatz für andere Umwandlungen verwendet werden, kann aber aufgrund der Fähigkeit, in einen Prozess zu gelangen, extrem gefährlich sein reinterpret_cast, und letzteres sollte bevorzugt werden, wenn explizites Casting benötigt wird, es sei denn, Sie sind sich sicher static_cast wird gelingen oder reinterpret_cast wird versagen. Berücksichtigen Sie auch dann die längere, explizitere Option.

C-Style-Umwandlungen ignorieren auch die Zugriffskontrolle, wenn a ausgeführt wird static_cast, was bedeutet, dass sie die Fähigkeit haben, eine Operation auszuführen, die kein anderer Cast kann. Dies ist jedoch meistens ein Klud, und in meinen Augen ist das nur ein weiterer Grund, C-artige Würfe zu vermeiden.


2207
2017-12-01 20:22



Benutzen dynamic_cast zum Konvertieren von Zeigern / Referenzen innerhalb einer Vererbungshierarchie.

Benutzen static_cast für normale Typkonvertierungen.

Benutzen reinterpret_cast zur Neuinterpretation von Bitmustern auf niedriger Ebene. Verwenden Sie mit äußerster Vorsicht.

Benutzen const_cast zum wegwerfen const/volatile. Vermeiden Sie dies, es sei denn, Sie stecken fest mit einer Const-inkorrekten API.


283
2018-01-21 04:53



(Eine Menge theoretischer und konzeptioneller Erklärungen wurde oben gegeben) 

Unten sind einige der praktische Beispiele als ich gebraucht habe static_cast, dynamic_cast, const_cast, reinterpret_cast.

(Bezieht sich auch auf die Erklärung zu verstehen: http://www.cplusplus.com/doc/tutorial/Typecasting/)

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

151
2017-12-11 02:05



Es könnte hilfreich sein, wenn du ein bisschen Interna weißt ...

static_cast

  • Der C ++ - Compiler kann bereits Scaler-Typen wie float in int umwandeln. Verwenden Sie static_cast für sie.
  • Im Allgemeinen würde static_cast beim Konvertieren von Typ A in B den Konstruktor von B aufrufen und A übergeben. Wenn B keinen solchen Konstruktor hat, dann erhalten Sie einen Kompilierzeitfehler.
  • Abgeworfen A* zu B* ist immer erfolgreich, wenn A und B in der Vererbungshierarchie (oder void) sind, sonst erhalten Sie einen Kompilierungsfehler.
  • Erwischt: Wenn Sie den Basiszeiger auf den abgeleiteten Zeiger anwenden, aber wenn das tatsächliche Objekt kein abgeleiteter Typ ist, dann Sie nicht Fehler bekommen. Sie erhalten einen schlechten Zeiger und sobald Sie versuchen, auf die Mitglieder des abgeleiteten Zeigers zuzugreifen, erhalten Sie zur Laufzeit segfault.
  • Gleiches gilt für A& zu B&.
  • Erwischt: Cast von Derived zu Base oder umgekehrt erstellt neue Kopie! Für Leute, die von C # / Java kommen, können viele von oben eine große Überraschung sein.

dynamic_cast

  • dynamic_cast verwendet Informationen zum Laufzeittyp, um herauszufinden, ob die Umwandlung gültig ist. Beispielsweise, (Base*) zu (Derived*)kann fehlschlagen, wenn der Zeiger nicht vom abgeleiteten Typ ist.
  • Das heißt, dynamic_cast ist im Vergleich zu static_cast sehr teuer!
  • Zum A* zu B*Wenn die Umwandlung ungültig ist, gibt dynamic_cast nullptr zurück.
  • Zum A& zu B& Wenn die Umwandlung ungültig ist, wird dynamic_cast die Ausnahme bad_cast auslösen.
  • Im Gegensatz zu anderen Umwandlungen gibt es Laufzeit-Overhead.

const_cast

  • Während static_cast nicht-const zu const machen kann, kann es nicht anders herum gehen. Der const_cast kann beides tun.
  • Ein Beispiel, wo dies nützlich ist, ist das Durchlaufen einiger Container wie set<T> die nur ihre Elemente als const zurückgibt, um sicherzustellen, dass Sie ihren Schlüssel nicht ändern. Wenn Sie jedoch die Nicht-Schlüsselelemente des Objekts ändern möchten, sollte es in Ordnung sein. Sie können const_cast verwenden, um die Konstanz zu entfernen.
  • Ein anderes Beispiel ist, wenn Sie implementieren möchten T& foo() ebenso gut wie const T& foo(). Um Code-Duplizierung zu vermeiden, können Sie const_cast anwenden, um den Wert einer Funktion von einem anderen zurückzugeben.

reinterpret_cast

  • Dies bedeutet im Grunde genommen, dass diese Bytes an dieser Speicherstelle genommen werden und sie als gegebenes Objekt betrachten.
  • Zum Beispiel können Sie 4 Bytes float auf 4 Bytes von int laden, um zu sehen, wie Bits in float aussehen.
  • Offensichtlich, wenn Daten für den Typ nicht korrekt sind, können Sie segfault erhalten.
  • Es gibt keinen Laufzeit-Overhead für diese Besetzung.

51
2017-12-01 20:20



Tut Dies beantworte deine Frage?

Ich habe es nie benutzt reinterpret_castund frage mich, ob es nicht ein schlechtes Design ist, in einen Fall zu rennen, der es braucht. In der Codebasis arbeite ich weiter dynamic_cast wird viel benutzt. Der Unterschied mit static_cast ist das ein dynamic_cast überprüft die Laufzeit, was (sicherer) oder nicht (mehr Overhead) sein kann, was Sie wollen (siehe msdn).


11
2018-05-31 14:16



Zusätzlich zu den anderen Antworten bisher, hier ist das Beispiel wo nicht static_cast ist nicht genug, dass reinterpret_cast wird gebraucht. Angenommen, es gibt eine Funktion, die in einem Ausgabeparameter Zeiger auf Objekte verschiedener Klassen zurückgibt (die keine gemeinsame Basisklasse haben). Ein echtes Beispiel für eine solche Funktion ist CoCreateInstance() (Siehe den letzten Parameter, der tatsächlich ist void**). Angenommen, Sie fordern eine bestimmte Objektklasse von dieser Funktion an, damit Sie im Voraus den Typ für den Zeiger kennen (was Sie häufig für COM-Objekte tun). In diesem Fall können Sie keinen Pointer auf Ihren Zeiger werfen void** mit static_cast: du brauchst 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) );

Jedoch, static_cast funktioniert für einfache Zeiger (nicht Zeiger auf Zeiger), so dass der obige Code neu geschrieben werden kann, um zu vermeiden reinterpret_cast (zu einem Preis einer zusätzlichen Variable) auf folgende Weise:

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

9