Frage Regulärer Cast vs. Static_cast vs. Dynamic_cast [Duplizieren]


Diese Frage hat hier bereits eine Antwort:

Ich schreibe seit fast zwanzig Jahren C- und C ++ - Code, aber es gibt einen Aspekt dieser Sprachen, den ich nie wirklich verstanden habe. Ich habe offensichtlich regelmäßige Würfe benutzt, d.h.

MyClass *m = (MyClass *)ptr;

überall, aber es scheint zwei andere Arten von Güssen zu geben, und ich kenne den Unterschied nicht. Was ist der Unterschied zwischen den folgenden Codezeilen?

MyClass *m = (MyClass *)ptr;
MyClass *m = static_cast<MyClass *>(ptr);
MyClass *m = dynamic_cast<MyClass *>(ptr);

1468
2017-08-26 13:20


Ursprung


Antworten:


static_cast

static_cast wird für Fälle verwendet, in denen Sie eine implizite Konvertierung mit einigen Einschränkungen und Ergänzungen grundsätzlich rückgängig machen möchten. static_cast führt keine Laufzeitprüfungen durch. Dies sollte verwendet werden, wenn Sie wissen, dass Sie auf ein Objekt eines bestimmten Typs verweisen und somit eine Überprüfung unnötig wäre. Beispiel:

void func(void *data) {
  // Conversion from MyClass* -> void* is implicit
  MyClass *c = static_cast<MyClass*>(data);
  ...
}

int main() {
  MyClass c;
  start_thread(&func, &c)  // func(&c) will be called
      .join();
}

In diesem Beispiel wissen Sie, dass Sie eine bestanden haben MyClass Objekt, und somit ist keine Laufzeitprüfung erforderlich, um dies zu gewährleisten.

dynamic_cast

dynamic_cast Ist nützlich, wenn Sie nicht wissen, wie der dynamische Typ des Objekts ist. Sie gibt einen Nullzeiger zurück, wenn das Objekt, auf das verwiesen wird, den Typ nicht enthält, der als Basisklasse bestimmt ist (wenn Sie auf eine Referenz werfen, a bad_cast Ausnahme wird in diesem Fall geworfen).

if (JumpStm *j = dynamic_cast<JumpStm*>(&stm)) {
  ...
} else if (ExprStm *e = dynamic_cast<ExprStm*>(&stm)) {
  ...
}

Sie können nicht verwenden dynamic_cast Wenn Sie einen Downcast durchführen (in eine abgeleitete Klasse umwandeln) und der Argumenttyp nicht polymorph ist. Zum Beispiel ist der folgende Code nicht gültig, weil Base enthält keine virtuelle Funktion:

struct Base { };
struct Derived : Base { };
int main() {
  Derived d; Base *b = &d;
  dynamic_cast<Derived*>(b); // Invalid
}

Ein "Upcast" (Cast in die Basisklasse) ist immer für beide gültig static_cast und dynamic_cast, und auch ohne jede Umwandlung, da ein "Up-Cast" eine implizite Konvertierung ist.

Regelmäßige Besetzung

Diese Modelle werden auch C-Style-Cast genannt. Ein C-Style-Cast ist im Grunde genommen identisch mit dem Testen einer Reihe von C ++ - Casstypen und dem ersten C ++ - Cast, der funktioniert, ohne jemals in Betracht zu ziehen dynamic_cast. Es ist unnötig zu sagen, dass dies viel stärker ist, da es alle verbindet const_cast, static_cast und reinterpret_cast, aber es ist auch unsicher, weil es nicht verwendet wird dynamic_cast.

Darüber hinaus können Sie mit C-artigen Umwandlungen nicht nur das tun, sondern Sie können auch sicher in eine private Basisklasse umwandeln, während das "Äquivalent" static_cast Sequenz würde Ihnen einen Fehler bei der Kompilierung geben.

Manche Leute bevorzugen C-Style-Casts wegen ihrer Kürze. Ich verwende sie nur für numerische Umwandlungen und verwende die entsprechenden C ++ - Umwandlungen, wenn benutzerdefinierte Typen betroffen sind, da sie eine strengere Überprüfung ermöglichen.


1413
2017-08-10 13:50



Statische Besetzung

Die statische Umwandlung führt Konvertierungen zwischen kompatiblen Typen durch. Es ist ähnlich dem C-Style-Cast, ist aber restriktiver. Zum Beispiel würde der C-Style-Cast einem Ganzzahlzeiger erlauben, auf ein Zeichen zu zeigen.

char c = 10;       // 1 byte
int *p = (int*)&c; // 4 bytes

Da dies zu einem 4-Byte-Zeiger führt, der auf 1 Byte zugewiesenen Speicher zeigt, führt das Schreiben in diesen Zeiger entweder zu einem Laufzeitfehler oder zum Überschreiben eines benachbarten Speichers.

*p = 5; // run-time error: stack corruption

Im Gegensatz zum C-Style-Cast ermöglicht der statische Cast dem Compiler, zu überprüfen, ob die Zeiger- und Zeigerdatentypen kompatibel sind. Dadurch kann der Programmierer diese falsche Pointer-Zuweisung während der Kompilierung abfangen.

int *q = static_cast<int*>(&c); // compile-time error

Reinterpretation von Cast

Um die Zeigerkonvertierung zu erzwingen, wird auf die gleiche Weise wie beim C-Style-Cast im Hintergrund der Re-Interpretieren-Cast verwendet.

int *r = reinterpret_cast<int*>(&c); // forced conversion

Diese Umwandlung behandelt Umwandlungen zwischen bestimmten nicht verwandten Typen, z. B. von einem Zeigertyp zu einem anderen inkompatiblen Zeigertyp. Es führt einfach eine binäre Kopie der Daten aus, ohne das zugrunde liegende Bitmuster zu verändern. Beachten Sie, dass das Ergebnis einer solchen Low-Level-Operation systemspezifisch und daher nicht portabel ist. Es sollte mit Vorsicht verwendet werden, wenn es nicht vollständig vermieden werden kann.

Dynamische Besetzung

Dieser wird nur zum Konvertieren von Objektzeigern und Objektreferenzen in andere Zeiger- oder Referenztypen in der Vererbungshierarchie verwendet. Es ist die einzige Umwandlung, die sicherstellt, dass das Objekt, auf das verwiesen wird, konvertiert werden kann, indem eine Laufzeitprüfung durchgeführt wird, bei der der Zeiger auf ein vollständiges Objekt des Zieltyps verweist. Damit diese Laufzeitprüfung möglich ist, muss das Objekt polymorph sein. Das heißt, die Klasse muss mindestens eine virtuelle Funktion definieren oder erben. Dies liegt daran, dass der Compiler nur die benötigte Laufzeittypinformation für solche Objekte erzeugt.

Beispiele für dynamische Beispiele

Im folgenden Beispiel wird ein MyChild-Zeiger mithilfe eines dynamischen Cast in einen MyBase-Zeiger konvertiert. Diese Konvertierung von abgeleitet nach Basis ist erfolgreich, weil das untergeordnete Objekt ein vollständiges Basisobjekt enthält.

class MyBase 
{ 
  public:
  virtual void test() {}
};
class MyChild : public MyBase {};



int main()
{
  MyChild *child = new MyChild();
  MyBase  *base = dynamic_cast<MyBase*>(child); // ok
}

Das nächste Beispiel versucht, einen MyBase-Zeiger in einen MyChild-Zeiger zu konvertieren. Da das Base-Objekt kein vollständiges Child-Objekt enthält, schlägt diese Pointer-Konvertierung fehl. Um dies anzuzeigen, gibt der dynamische Darsteller einen Nullzeiger zurück. Dies bietet eine bequeme Möglichkeit zu überprüfen, ob eine Konvertierung während der Laufzeit erfolgreich war.

MyBase  *base = new MyBase();
MyChild *child = dynamic_cast<MyChild*>(base);


if (child == 0) 
std::cout << "Null pointer returned";

Wenn anstelle eines Zeigers eine Referenz konvertiert wird, schlägt der dynamische Cast dann fehl, indem eine bad_cast-Ausnahme ausgelöst wird. Dies muss mit einer try-catch-Anweisung behandelt werden.

#include <exception>
// …  
try
{ 
  MyChild &child = dynamic_cast<MyChild&>(*base);
}
catch(std::bad_cast &e) 
{ 
  std::cout << e.what(); // bad dynamic_cast
}

Dynamischer oder statischer Cast

Der Vorteil der dynamischen Umwandlung besteht darin, dass der Programmierer überprüfen kann, ob eine Konvertierung während der Laufzeit erfolgreich war oder nicht. Der Nachteil besteht darin, dass mit dieser Überprüfung ein Leistungsaufwand verbunden ist. Aus diesem Grund wäre die Verwendung eines statischen Cast im ersten Beispiel vorzuziehen, da eine Konvertierung von abgeleitet zu Base niemals fehlschlagen wird.

MyBase *base = static_cast<MyBase*>(child); // ok

Im zweiten Beispiel kann die Konvertierung jedoch erfolgreich sein oder fehlschlagen. Es wird fehlschlagen, wenn das MyBase-Objekt eine MyBase-Instanz enthält und wenn es eine MyChild-Instanz enthält, wird es erfolgreich sein. In einigen Situationen kann dies bis zur Laufzeit nicht bekannt sein. Wenn dies der Fall ist, ist die dynamische Besetzung eine bessere Wahl als die statische Besetzung.

// Succeeds for a MyChild object
MyChild *child = dynamic_cast<MyChild*>(base);

Wenn die Konvertierung von Base zu abgeleitet mit einem statischen Cast statt einem dynamischen Cast durchgeführt wurde, wäre die Konvertierung nicht fehlgeschlagen. Es hätte einen Zeiger zurückgegeben, der auf ein unvollständiges Objekt verweist. Das Dereferenzieren eines solchen Zeigers kann zu Laufzeitfehlern führen.

// Allowed, but invalid
MyChild *child = static_cast<MyChild*>(base);

// Incomplete MyChild object dereferenced
(*child);

Const Besetzung

Dieser wird hauptsächlich verwendet, um den const-Modifizierer einer Variablen hinzuzufügen oder zu entfernen.

const int myConst = 5;
int *nonConst = const_cast<int*>(&myConst); // removes const

Obwohl const cast den Wert einer Konstante ändern kann, ist dies immer noch ein ungültiger Code, der einen Laufzeitfehler verursachen kann. Dies könnte beispielsweise auftreten, wenn sich die Konstante in einem Abschnitt des Nur-Lese-Speichers befand.

*nonConst = 10; // potential run-time error

Const cast wird stattdessen hauptsächlich verwendet, wenn eine Funktion ein nicht konstantes Zeigerargument verwendet, obwohl sie den Zeiger nicht ändert.

void print(int *p) 
{
   std::cout << *p;
}

Die Funktion kann dann mithilfe einer Const-Umwandlung an eine konstante Variable übergeben werden.

print(&myConst); // error: cannot convert 
                 // const int* to int*

print(nonConst); // allowed

Quelle und weitere Erläuterungen


116
2017-08-26 13:28



Sie sollten sich den Artikel ansehen C ++ Programmierung / Typumwandlung.

Es enthält eine gute Beschreibung aller verschiedenen Gussarten. Das Folgende aus dem obigen Link:

const_cast

const_cast (Ausdruck) Der Befehl const_cast <> () wird zum Hinzufügen / Entfernen verwendet   const (ness) (oder Volatilität) einer Variablen.

static_cast

static_cast (Ausdruck) Der Befehl static_cast <> () wird zum Umwandeln verwendet   die Integer-Typen. 'z.B.' char-> lang, int-> kurz usw.

Statische Umwandlung wird auch verwendet, um Zeiger auf verwandte Typen, z   Beispiel casting void * auf den entsprechenden Typ.

dynamic_cast

Dynamic Cast wird verwendet, um Zeiger und Referenzen zur Laufzeit zu konvertieren,   in der Regel zu dem Zweck, einen Zeiger oder eine Referenz nach oben oder unten zu werfen   eine Vererbungskette (Vererbungshierarchie).

dynamic_cast (Ausdruck)

Der Zieltyp muss ein Zeiger oder Referenztyp sein, und der   Ausdruck muss zu einem Zeiger oder einer Referenz ausgewertet werden. Dynamische Besetzung funktioniert   nur wenn der Objekttyp, auf den sich der Ausdruck bezieht, ist   kompatibel mit dem Zieltyp und die Basisklasse hat mindestens eine   virtuelle Mitgliedsfunktion. Wenn nicht und der Typ des Ausdrucks, der gerade umgewandelt wird   Ist ein Zeiger, wird NULL zurückgegeben, wenn ein dynamischer Cast auf einen Verweis erfolgt   fehlschlägt, wird eine bad_cast-Ausnahme ausgelöst. Wenn es nicht scheitert, dynamisch   cast gibt einen Zeiger oder eine Referenz des Zieltyps auf das Objekt zurück   auf welchen Ausdruck bezogen.

reinterpret_cast

Reinterpretcast konvertiert einen Typ bitweise in einen anderen. Irgendein Zeiger   oder integraler Typ kann mit jedem anderen gegossen werden,   leicht Missbrauch zuzulassen. Zum Beispiel mit Neuinterpretation eine   könnte, unsicher, einen Ganzzahlzeiger auf einen Zeichenfolgenzeiger werfen.


71
2017-09-19 17:30



Vermeiden Sie C-Style-Modelle.

C-Style-Casts sind eine Mischung aus Const- und Reinterpret-Cast, und es ist schwierig, sie in Ihrem Code zu finden und zu ersetzen. Ein C ++ - Anwendungsprogrammierer sollte C-Style-Cast vermeiden.


23
2017-08-26 13:39



FYI, ich glaube, Bjarne Stroustrup wird zitiert, dass C-Style-Casts zu vermeiden sind und dass Sie static_cast oder dynamic_cast verwenden sollten, wenn irgend möglich.

Barne Stroustrups C ++ - Stil FAQ

Nimm den Rat für das, was du willst. Ich bin weit davon entfernt, ein C ++ - Guru zu sein.


21
2017-08-26 13:38



C-Style-Umwandlungen constate const_cast, static_cast und reinterpret_cast.

Ich wünschte, C ++ hätte keine C-Style Casts. C ++ - Umwandlungen heben sich deutlich von anderen ab (wie sie sollten; Umwandlungen zeigen normalerweise an, dass etwas schlecht gemacht wird) und unterscheiden richtig zwischen den verschiedenen Umwandlungsarten, die die Umwandlungen durchführen. Sie erlauben auch, dass ähnlich aussehende Funktionen geschrieben werden, z. boost :: lexical_cast, was aus Konsistenzsicht recht nett ist.


10
2017-08-26 13:26



dynamic_casthat Laufzeittyp-Prüfung und funktioniert nur mit Referenzen und Zeigern, während static_cast bietet keine Laufzeittypüberprüfung an. Weitere Informationen finden Sie im MSDN-Artikel static_cast Operator.


8
2018-02-05 17:10



dynamic_cast unterstützt nur Zeiger und Referenztypen. Es kehrt zurück NULL wenn der Cast unmöglich ist, wenn der Typ ein Zeiger ist oder eine Ausnahme auslöst, wenn der Typ ein Referenztyp ist. Daher, dynamic_cast kann verwendet werden, um zu prüfen, ob ein Objekt von einem bestimmten Typ ist, static_cast kann nicht (Sie werden einfach mit einem ungültigen Wert enden).

C-Style (und andere) Casts wurden in den anderen Antworten behandelt.


7