Stammbesetzung vs.static_cast vs.dynamic_cast [Duplikat]
Frage
Auf diese Frage gibt es 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 normale Abgüsse verwendet, d. h.
MyClass *m = (MyClass *)ptr;
überall, aber es scheint zwei andere Arten von Besetzungen 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);
Lösung
static_cast
static_cast
wird für Fälle verwendet, in denen Sie eine implizite Konvertierung grundsätzlich mit einigen Einschränkungen und Ergänzungen rückgängig machen möchten. static_cast
führt keine Laufzeitprüfungen durch.Dies sollte verwendet werden, wenn Sie wissen, dass Sie sich auf ein Objekt eines bestimmten Typs beziehen und eine Überprüfung daher nicht erforderlich 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 a bestanden haben MyClass
Objekt, und daher ist keine Laufzeitprüfung erforderlich, um dies sicherzustellen.
dynamischer_cast
dynamic_cast
ist nützlich, wenn Sie den dynamischen Typ des Objekts nicht kennen.Es gibt einen Nullzeiger zurück, wenn das Objekt, auf das verwiesen wird, nicht den als Basisklasse umgewandelten Typ enthält (bei der Umwandlung in eine Referenz a bad_cast
In diesem Fall wird eine Ausnahme ausgelöst.
if (JumpStm *j = dynamic_cast<JumpStm*>(&stm)) {
...
} else if (ExprStm *e = dynamic_cast<ExprStm*>(&stm)) {
...
}
Sie können es nicht verwenden dynamic_cast
wenn Sie ein Downcast durchführen (in eine abgeleitete Klasse umwandeln) und der Argumenttyp nicht polymorph ist.Der folgende Code ist beispielsweise ungü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 „Up-Cast“ (Umwandlung in die Basisklasse) ist bei beiden immer gültig static_cast
Und dynamic_cast
, und auch ohne Besetzung, da eine „Up-Cast“ eine implizite Konvertierung ist.
Regelmäßige Besetzung
Diese Abgüsse werden auch C-Stil-Abgüsse genannt.Eine Umwandlung im C-Stil ist im Grunde identisch mit dem Ausprobieren einer Reihe von Sequenzen von C++-Umsetzungen und der Übernahme der ersten C++-Umwandlung, die funktioniert, ohne jemals darüber nachzudenken dynamic_cast
.Es versteht sich von selbst, dass dies viel leistungsfähiger ist, da es alles vereint const_cast
, static_cast
Und reinterpret_cast
, aber es ist auch unsicher, weil es nicht verwendet wird dynamic_cast
.
Darüber hinaus ermöglichen Ihnen Umwandlungen im C-Stil nicht nur dies, sondern auch die sichere Umwandlung in eine private Basisklasse, während das „Äquivalent“ static_cast
Die Sequenz würde dafür einen Fehler bei der Kompilierung auslösen.
Manche Leute bevorzugen C-artige Abgüsse wegen ihrer Kürze.Ich verwende sie nur für numerische Umwandlungen und verwende die entsprechenden C++-Umwandlungen, wenn benutzerdefinierte Typen beteiligt sind, da sie eine strengere Prüfung ermöglichen.
Andere Tipps
Statische Besetzung
Die statische Umwandlung führt Konvertierungen zwischen kompatiblen Typen durch.Es ähnelt der Besetzung im C-Stil, ist jedoch restriktiver.Die Umwandlung im C-Stil würde es beispielsweise ermöglichen, dass ein ganzzahliger Zeiger auf ein Zeichen zeigt.
char c = 10; // 1 byte
int *p = (int*)&c; // 4 bytes
Da dies dazu führt, dass ein 4-Byte-Zeiger auf 1 Byte des zugewiesenen Speichers zeigt, führt das Schreiben auf diesen Zeiger entweder zu einem Laufzeitfehler oder zum Überschreiben eines angrenzenden Speichers.
*p = 5; // run-time error: stack corruption
Im Gegensatz zur Umwandlung im C-Stil ermöglicht die statische Umwandlung dem Compiler, zu überprüfen, ob die Zeiger- und Pointee-Datentypen kompatibel sind, wodurch der Programmierer diese falsche Zeigerzuweisung während der Kompilierung erkennen kann.
int *q = static_cast<int*>(&c); // compile-time error
Besetzung neu interpretieren
Um die Zeigerkonvertierung zu erzwingen, würde auf die gleiche Weise wie bei der Umwandlung im C-Stil im Hintergrund stattdessen die Neuinterpretation verwendet werden.
int *r = reinterpret_cast<int*>(&c); // forced conversion
Diese Umwandlung verarbeitet Konvertierungen zwischen bestimmten nicht verwandten Typen, beispielsweise von einem Zeigertyp in einen anderen inkompatiblen Zeigertyp.Es wird einfach eine binäre Kopie der Daten erstellt, ohne das zugrunde liegende Bitmuster zu ändern.Beachten Sie, dass das Ergebnis einer solchen Low-Level-Operation systemspezifisch und daher nicht portierbar ist.Es sollte mit Vorsicht angewendet werden, wenn es nicht ganz 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 gezeigt wird, konvertiert werden kann, indem zur Laufzeit überprüft wird, ob 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 erforderlichen Laufzeittypinformationen für solche Objekte generiert.
Dynamische Besetzungsbeispiele
Im folgenden Beispiel wird ein MyChild-Zeiger mithilfe einer dynamischen Umwandlung in einen MyBase-Zeiger umgewandelt.Diese Konvertierung vom abgeleiteten zum Basisobjekt ist erfolgreich, da 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
}
Im nächsten Beispiel wird versucht, einen MyBase-Zeiger in einen MyChild-Zeiger umzuwandeln.Da das Basisobjekt kein vollständiges untergeordnetes Objekt enthält, schlägt diese Zeigerkonvertierung fehl.Um dies anzuzeigen, gibt die dynamische Umwandlung einen Nullzeiger zurück.Dies bietet eine bequeme Möglichkeit, zur Laufzeit zu überprüfen, ob eine Konvertierung erfolgreich war oder nicht.
MyBase *base = new MyBase();
MyChild *child = dynamic_cast<MyChild*>(base);
if (child == 0)
std::cout << "Null pointer returned";
Wenn eine Referenz anstelle eines Zeigers konvertiert wird, schlägt die dynamische Umwandlung fehl, indem eine bad_cast-Ausnahme ausgelöst wird.Dies muss mithilfe einer Try-Catch-Anweisung gehandhabt werden.
#include <exception>
// …
try
{
MyChild &child = dynamic_cast<MyChild&>(*base);
}
catch(std::bad_cast &e)
{
std::cout << e.what(); // bad dynamic_cast
}
Dynamische oder statische Besetzung
Der Vorteil der Verwendung einer dynamischen Umwandlung besteht darin, dass der Programmierer während der Laufzeit überprüfen kann, ob eine Konvertierung erfolgreich war oder nicht.Der Nachteil besteht darin, dass mit der Durchführung dieser Prüfung ein Leistungsaufwand verbunden ist.Aus diesem Grund wäre die Verwendung einer statischen Umwandlung im ersten Beispiel vorzuziehen gewesen, da eine Konvertierung von abgeleiteten in eine Basis niemals fehlschlagen wird.
MyBase *base = static_cast<MyBase*>(child); // ok
Im zweiten Beispiel kann die Konvertierung jedoch entweder erfolgreich sein oder fehlschlagen.Es schlägt fehl, wenn das MyBase-Objekt eine MyBase-Instanz enthält, und ist erfolgreich, wenn es eine MyChild-Instanz enthält.In manchen Situationen ist dies möglicherweise erst zur Laufzeit bekannt.In diesem Fall ist die dynamische Umwandlung eine bessere Wahl als die statische Umwandlung.
// Succeeds for a MyChild object
MyChild *child = dynamic_cast<MyChild*>(base);
Wenn die Konvertierung von der Basis in eine abgeleitete Konvertierung mithilfe einer statischen Umwandlung anstelle einer dynamischen Umwandlung durchgeführt worden wäre, 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-Modifikator einer Variablen hinzuzufügen oder zu entfernen.
const int myConst = 5;
int *nonConst = const_cast<int*>(&myConst); // removes const
Obwohl const cast die Änderung des Werts einer Konstante zulässt, handelt es sich hierbei immer noch um ungültigen Code, der einen Laufzeitfehler verursachen kann.Dies kann beispielsweise der Fall sein, wenn sich die Konstante in einem Abschnitt des Nur-Lese-Speichers befindet.
*nonConst = 10; // potential run-time error
Const Cast wird stattdessen hauptsächlich verwendet, wenn es eine Funktion gibt, die ein nicht konstantes Zeigerargument akzeptiert, auch wenn sie den Zeiger nicht ändert.
void print(int *p)
{
std::cout << *p;
}
Der Funktion kann dann mithilfe einer const-Umwandlung eine konstante Variable übergeben werden.
print(&myConst); // error: cannot convert
// const int* to int*
print(nonConst); // allowed
Du solltest dir den Artikel ansehen C++-Programmierung/Typumwandlung.
Es enthält eine gute Beschreibung aller verschiedenen Besetzungstypen.Folgendes ist dem obigen Link entnommen:
const_cast
const_cast (Ausdruck) Der const_cast <> () wird verwendet, um const (ness) (oder volatile) einer Variablen hinzuzufügen/zu entfernen.
static_cast
static_cast (Ausdruck) Mit dem static_cast <> () werden zwischen den Ganzzahltypen gegossen.'EG' char-> lang, int-> kurz usw.
Statische Besetzung wird auch verwendet, um Zeiger auf verwandte Typen zu gießen, z. B. void* auf den entsprechenden Typ.
dynamischer_cast
Dynamische Besetzung wird verwendet, um Zeigungen und Referenzen zur Laufzeit zu konvertieren, um einen Zeiger oder Referenz in eine Erbskette (Erbschaftsarchie) nach oben oder unten zu werfen.
Dynamic_cast(Ausdruck)
Der Zieltyp muss ein Zeiger oder einen Referenztyp sein, und der Ausdruck muss an einen Zeiger oder eine Referenz bewertet werden.Dynamic Cast funktioniert nur, wenn der Objekttyp, auf den sich der Ausdruck bezieht, mit dem Zieltyp kompatibel ist und die Basisklasse mindestens eine virtuelle Mitgliedsfunktion hat.Wenn nicht, und die Art des Ausdrucks ist ein Zeiger, wird Null zurückgegeben. Wenn eine dynamische Besetzung auf einer Referenz fehlschlägt, wird eine Bad_cast -Ausnahme ausgelöst.Wenn es nicht fehlschlägt, gibt der dynamische Cast einen Zeiger oder eine Referenz des Zieltyps auf das Objekt zurück, auf das sich der Ausdruck verwiesen.
reinterpret_cast
Beim Reinterpret-Cast wird einfach ein Typ bitweise in einen anderen umgewandelt.Jeder Zeiger oder ein integrierter Typ kann mit neuem Cast an jeden anderen gegossen werden, wodurch leicht Missbrauch ermöglicht werden kann.Zum Beispiel mit neu interpretierender Besetzung könnte sich unsicher einen Ganzzahlzeiger auf einen Streichzeiger werfen.
Zu Ihrer Information: Ich glaube, Bjarne Stroustrup wird mit den Worten zitiert, dass Umwandlungen im C-Stil vermieden werden sollten und dass Sie nach Möglichkeit static_cast oder Dynamic_cast verwenden sollten.
Häufig gestellte Fragen zum C++-Stil von Barne Stroustrup
Nehmen Sie diesen Rat an, was Sie wollen.Ich bin weit davon entfernt, ein C++-Guru zu sein.
Vermeiden Sie die Verwendung von Abgüssen im C-Stil.
Casts im C-Stil 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 eine Umwandlung im C-Stil vermeiden.
C-artige Umwandlungen verschmelzen const_cast, static_cast und reinterpret_cast.
Ich wünschte, C++ hätte keine Umwandlungen im C-Stil.C++-Umsetzungen stechen richtig hervor (wie sie sollten;Casts deuten normalerweise darauf hin, dass man etwas Schlimmes getan hat) und unterscheiden richtig zwischen den verschiedenen Arten der Konvertierung, die Casts durchführen.Sie ermöglichen auch das Schreiben ähnlich aussehender Funktionen, z.boost::lexical_cast, was aus Sicht der Konsistenz ganz nett ist.
dynamic_cast
verfügt über eine Laufzeittypprüfung und funktioniert nur mit Referenzen und Zeigern static_cast
bietet keine Laufzeittypprüfung.Vollständige Informationen finden Sie im MSDN-Artikel static_cast-Operator.
dynamic_cast
Unterstützt nur Zeiger- und Referenztypen.Es kehrt zurück NULL
wenn die Umwandlung unmöglich ist, wenn der Typ ein Zeiger ist, oder eine Ausnahme auslöst, wenn der Typ ein Referenztyp ist.Somit, dynamic_cast
kann verwendet werden, um zu überprüfen, ob ein Objekt von einem bestimmten Typ ist, static_cast
nicht möglich (am Ende erhalten Sie einfach einen ungültigen Wert).
C-artige (und andere) Besetzungen wurden in den anderen Antworten behandelt.