[c++] Regulärer Cast vs. Static_cast vs. Dynamic_cast



Answers

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

Question

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 verwendet, dh

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



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, ähnlich aussehende Funktionen zu schreiben, zB boost :: lexical_cast, was aus Konsistenzsicht recht nett ist.




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.




dynamic_cast unterstützt nur Zeiger- und Referenztypen. Es gibt NULL wenn die Umwandlung unmöglich ist, wenn der Typ ein Zeiger ist, oder eine Ausnahme auslöst, wenn der Typ ein Referenztyp ist. Daher kann dynamic_cast verwendet werden, um zu überprüfen, ob ein Objekt von einem bestimmten Typ ist, was static_cast nicht kann (Sie erhalten einfach einen ungültigen Wert).

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






Related