c++ reinterpret_cast - Regulärer Cast vs. Static_cast vs. Dynamic_cast





performance cpp (8)


Sie sollten sich den Artikel C ++ - Programmierung / Typcasting anschauen.

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

const_cast

const_cast (Ausdruck) Mit dem Befehl const_cast <> () wird const (ness) (oder volatile-ness) einer Variablen hinzugefügt / entfernt.

static_cast

static_cast (Ausdruck) Mit dem Befehl static_cast <> () wird zwischen den Integer-Typen gewandelt. 'zB' char-> lang, int-> kurz usw.

Statischer Cast wird auch verwendet, um Zeiger auf verwandte Typen zu werfen, zum Beispiel casting void * auf den entsprechenden Typ.

dynamic_cast

Dynamic Cast wird verwendet, um Zeiger und Referenzen zur Laufzeit zu konvertieren, im Allgemeinen zum Zweck, einen Zeiger oder eine Referenz nach oben oder unten in eine Vererbungskette (Vererbungshierarchie) umzuwandeln.

dynamic_cast (Ausdruck)

Der Zieltyp muss ein Zeiger oder Referenztyp sein, und der Ausdruck muss einen Zeiger oder eine Referenz auswerten. Die dynamische Umwandlung funktioniert nur, wenn der Objekttyp, auf den sich der Ausdruck bezieht, mit dem Zieltyp kompatibel ist und die Basisklasse über mindestens eine virtuelle Elementfunktion verfügt. Wenn dies nicht der Fall ist und der Typ des auszugebenden Ausdrucks ein Zeiger ist, wird NULL zurückgegeben. Wenn ein dynamischer Cast für eine Referenz fehlschlägt, wird eine bad_cast-Ausnahme ausgelöst. Wenn dies nicht fehlschlägt, gibt dynamic cast einen Zeiger oder eine Referenz des Zieltyps an das Objekt zurück, auf das der Ausdruck verwiesen hat.

reinterpret_cast

Reinterpretcast konvertiert einen Typ bitweise in einen anderen. Irgendein Zeiger oder integraler Typ kann zu jedem anderen mit Reinterpretcast gegossen werden, leicht erlaubend Missbrauch. Zum Beispiel könnte man mit der Uminterpretation einen Integer-Zeiger auf einen String-Pointer werfen.

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



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.




static_cast

static_cast wird für Fälle verwendet, in denen Sie eine implizite Konvertierung mit einigen Einschränkungen und Ergänzungen im Prinzip 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 ein MyClass Objekt übergeben haben. MyClass 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. Es gibt einen Null-Zeiger zurück, wenn das Objekt, auf das verwiesen wird, den Typ nicht enthält, der als Basisklasse festgelegt ist (wenn Sie auf einen Verweis bad_cast wird in diesem Fall eine Ausnahme bad_cast ausgelöst).

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

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

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

Ein "up-cast" (Cast in die Basisklasse) ist immer gültig sowohl für static_cast als auch für dynamic_cast und auch ohne Cast, da es sich bei einer "up-cast" um eine implizite Konvertierung handelt.

Regelmäßige Besetzung

Diese Modelle werden auch C-Style-Cast genannt. Ein C-Style-Cast ist im Wesentlichen identisch mit dem Testen einer Reihe von C ++ - dynamic_cast Folgen und dem ersten C ++ - Cast, der funktioniert, ohne jemals dynamic_cast Betracht zu dynamic_cast . Es ist const_cast static_cast , dass dies viel mächtiger ist, da es alle const_cast von const_cast , static_cast und reinterpret_cast kombiniert, aber es ist auch unsicher, da es dynamic_cast nicht verwendet.

Darüber hinaus können Sie mit C-Style-Umwandlungen nicht nur das tun, sondern auch eine sichere static_cast in eine private Basisklasse durchführen, während die "äquivalente" static_cast Sequenz einen Fehler bei der Kompilierung static_cast würde.

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.




dynamic_cast hat eine Laufzeittypprüfung und arbeitet nur mit Referenzen und Zeigern, während static_cast keine Laufzeittypüberprüfung anbietet. Weitere Informationen finden Sie im MSDN-Artikel static_cast Operator .




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.




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




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.




Manchmal kann eine solche Frage in einem Interview gestellt werden.

Zum Beispiel, wenn Sie schreiben:

int a = 2;
long b = 3;
a = a + b;

Es gibt keine automatische Typumwandlung. In C ++ wird kein Fehler beim Kompilieren des obigen Codes Incompatible type exception . In Java wird jedoch eine Incompatible type exception .

Um dies zu vermeiden, müssen Sie Ihren Code folgendermaßen schreiben:

int a = 2;
long b = 3;
a += b;// No compilation error or any exception due to the auto typecasting




c++ pointers casting