Regulärer Cast vs. static_cast vs. dynamic_cast [duplicate]
Lesezeit: 12 Minuten
Graem Perrow
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 Besetzungen verwendet, dh
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?
Ich würde die Umwandlung im alten C-Stil in C++ nicht als “regulären Cast” bezeichnen, da es alles andere als ist. Sie sollten C++ im Allgemeinen nicht verwenden, insbesondere bei Klassen, da es einfach zu leicht ist, Fehler damit zu machen. Die Verwendung davon ist ein Zeichen für einen C-Programmierer, der zu C++ gewechselt ist, aber C++ noch nicht ganz gelernt hat.
– Hyde
30. Januar 2013 um 7:03 Uhr
Wie kann eine Frage mit Antwort ein Duplikat einer Frage ohne Antwort sein? mehr noch, diese Frage wurde früher gestellt als das “Original”
– Wladp
21. Juni 2015 um 8:28 Uhr
@Vladp Falls Sie sich immer noch fragen oder jemand anderes dies liest und sich wundert. (Außerdem, fürs Protokoll, es war kein Moderator, der dies geschlossen hat, sondern ein Benutzer mit einem Dupe-Hammer)
– Nik
1. Februar 2017 um 1:16 Uhr
Zu Ihrer Information, die verknüpfte Frage hat viel mehr positive Stimmen und die Antworten haben auch viel mehr positive Stimmen. Auch die verknüpfte Frage enthält einige gute nicht-theoretische Beispiele. (Außerdem bezieht sich die verknüpfte Frage nicht fälschlicherweise auf die Typumwandlungssyntax im C-Stil als “reguläre Umwandlung”.)
– Trevor Boyd Smith
7. März 2018 um 14:20 Uhr
Johannes Schaub – litb
static_cast
`static_cast` wird für Fälle verwendet, in denen Sie grundsätzlich eine implizite Konvertierung 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 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 a bestanden haben MyClass -Objekt, und daher ist keine Laufzeitprüfung erforderlich, um dies sicherzustellen.
dynamic_cast
`dynamic_cast` ist nützlich, wenn Sie den dynamischen Typ des Objekts nicht kennen. Es gibt einen Null-Zeiger zurück, wenn das Objekt, auf das verwiesen wird, nicht den Typ enthält, in den als Basisklasse gecastet wurde (wenn Sie in eine Referenz umwandeln, wird in diesem Fall eine ‘bad_cast’-Ausnahme ausgelöst).
if (JumpStm *j = dynamic_cast<JumpStm*>(&stm)) {
...
} else if (ExprStm *e = dynamic_cast<ExprStm*>(&stm)) {
...
}
Sie können nicht verwenden dynamic_cast wenn Sie downcasten (in eine abgeleitete Klasse umwandeln) und der Argumenttyp nicht polymorph ist. Der folgende Code ist beispielsweise 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 “up-cast” (cast auf die Basisklasse) ist immer bei beiden gültig static_cast und dynamic_castund auch ohne Cast, da ein “up-cast” eine implizite Konvertierung ist (vorausgesetzt, die Basisklasse ist zugänglich, dh es ist eine public Nachlass).
Normale Besetzung
Diese Casts werden auch als C-Style Cast bezeichnet. Eine Umwandlung im C-Stil ist im Grunde identisch mit dem Ausprobieren einer Reihe von Sequenzen von C++-Umwandlungen und dem Nehmen der ersten C++-Umwandlung, die funktioniert, ohne jemals darüber nachzudenken dynamic_cast. Unnötig zu erwähnen, dass dies viel leistungsfähiger ist, da es alle kombiniert const_cast, static_cast und reinterpret_castaber es ist auch unsicher, weil es nicht verwendet wird dynamic_cast.
Darüber hinaus können Sie mit C-Style-Casts nicht nur dies tun, sondern auch sicher in eine private Basisklasse umwandeln, während das “Äquivalent” static_cast Sequenz würde Ihnen dafür einen Kompilierzeitfehler geben.
Einige Leute bevorzugen wegen ihrer Kürze C-Style-Casts. 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 bieten.
@JohannesSchaub-litb: Sind Sie sicher, dass Sie mit einer Umwandlung im C-Stil „sicher“ in eine private Basisklasse umwandeln können? Ich kann sehen, dass das funktioniert, wenn die private Basisklasse die einzige /base/ ist, aber was ist mit virtueller/mehrfacher Vererbung? Ich gehe davon aus, dass die Besetzung im C-Stil keine Zeigermanipulation durchführt.
– Josef Garwin
29. Februar 2012 um 18:32 Uhr
@JohannesSchaub-litb Stimmt es, dass die Verwendung der alten C-Style-Casts gegenüber den C++-Casts auch einen gewissen Overhead mit sich bringt?
– xcrypt
10. Mai 2012 um 19:47 Uhr
@Joseph: Es wird kein Crosscast korrekt durchgeführt oder in einem der anderen Fälle, in denen eine Laufzeitprüfung erforderlich ist (dynamic_cast ist nötig). Aber es werden alle die gleichen Zeigeranpassungen wie static_cast tut. Mehrfache (nicht-virtuelle) Vererbung wird problemlos unterstützt, und die korrekte Zeigereinstellung wird verwendet.
– Ben Voigt
28. Dezember 2012 um 21:16 Uhr
Können Sie genauer erklären, warum der Downcast im dynamischen Besetzungsabschnitt ungültig ist? davon ausgehen Derived hatte ein member m Ich möchte erreichen, wie würde dies erreicht werden, wenn dynamic_cast ist keine Option?
– ted
9. März 2013 um 0:06 Uhr
Statische Besetzung
Die statische Umwandlung führt Konvertierungen zwischen kompatiblen Typen durch. Sie ähnelt der Besetzung im C-Stil, ist jedoch restriktiver. Zum Beispiel würde die Umwandlung im C-Stil einem Integer-Zeiger 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 des zugewiesenen Speichers zeigt, wird das Schreiben in diesen Zeiger entweder einen Laufzeitfehler verursachen oder angrenzenden Speicher überschreiben.
*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 Datentypen von Zeiger und Pointee kompatibel sind, wodurch der Programmierer diese falsche Zeigerzuweisung während der Kompilierung abfangen kann.
int *q = static_cast<int*>(&c); // compile-time error
Besetzung neu interpretieren
Um die Zeigerkonvertierung zu erzwingen, würde stattdessen die Umwandlung im C-Stil auf die gleiche Weise wie die Umwandlung im C-Stil im Hintergrund verwendet werden.
int *r = reinterpret_cast<int*>(&c); // forced conversion
Diese Umwandlung verarbeitet Konvertierungen 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 portierbar ist. Es sollte mit Vorsicht angewendet werden, wenn es nicht ganz vermieden werden kann.
Dynamische Besetzung
Dieser wird nur verwendet, um Objektzeiger und Objektreferenzen in andere Zeiger- oder Referenztypen in der Vererbungshierarchie umzuwandeln. Es ist die einzige Umwandlung, die sicherstellt, dass das Objekt, auf das gezeigt wird, konvertiert werden kann, indem eine Laufzeitprüfung durchgeführt wird, dass 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 a MyChild Zeiger wird in a umgewandelt MyBase Zeiger mit dynamischer Umwandlung. Diese Ableitung-zu-Basis-Konvertierung 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
}
Das nächste Beispiel versucht, a zu konvertieren MyBase Zeiger auf a MyChild Zeiger. 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. Auf diese Weise kann zur Laufzeit bequem überprüft werden, 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 anstelle eines Zeigers eine Referenz konvertiert wird, schlägt die dynamische Umwandlung fehl, indem a geworfen wird bad_cast Ausnahme. Dies muss mit a behandelt werden try-catch Aussage.
Der Vorteil der Verwendung eines dynamischen Casts 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 dieser Prüfung ein Leistungsaufwand verbunden ist. Aus diesem Grund wäre im ersten Beispiel die Verwendung eines statischen Casts vorzuziehen gewesen, da eine Konvertierung von abgeleitet nach Basis niemals fehlschlagen wird.
MyBase *base = static_cast<MyBase*>(child); // ok
Im zweiten Beispiel kann die Konvertierung jedoch entweder erfolgreich sein oder fehlschlagen. Es wird scheitern, wenn die MyBase Objekt enthält a MyBase Instanz und es wird erfolgreich sein, wenn es eine enthält MyChild Beispiel. In einigen Situationen kann dies bis zur Laufzeit nicht bekannt sein. In diesem Fall ist die dynamische Umwandlung die bessere Wahl als die statische Umwandlung.
// Succeeds for a MyChild object
MyChild *child = dynamic_cast<MyChild*>(base);
Wenn die Basis-zu-abgeleitete-Konvertierung unter Verwendung einer statischen Umwandlung anstelle einer dynamischen Umwandlung durchgeführt worden wäre, wäre die Umwandlung 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.
Dieser wird hauptsächlich verwendet, um die hinzuzufügen oder zu entfernen const Modifikator einer Variablen.
const int myConst = 5;
int *nonConst = const_cast<int*>(&myConst); // removes const
Obwohl const cast ermöglicht es, den Wert einer Konstante zu ändern, dies ist jedoch immer noch ungültiger Code, der einen Laufzeitfehler verursachen kann. Dies könnte beispielsweise auftreten, 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, obwohl es den Pointee nicht ändert.
void print(int *p)
{
std::cout << *p;
}
Der Funktion kann dann mit a eine konstante Variable übergeben werden const Gießen.
Es enthält eine gute Beschreibung aller verschiedenen Besetzungstypen. Folgendes aus obigem Link:
const_cast
const_cast(expression) const_cast<>() wird verwendet, um const(ness) (oder volatile-ness) einer Variablen hinzuzufügen/zu entfernen.
static_cast
static_cast(expression) Der static_cast<>() wird verwendet, um zwischen den Integer-Typen umzuwandeln. ‘eg’ char->long, int->short usw.
Die statische Umwandlung wird auch verwendet, um Zeiger auf verwandte Typen umzuwandeln, z. B. das Umwandeln von void* in den entsprechenden Typ.
dynamic_cast
Die dynamische Umwandlung wird verwendet, um Zeiger und Referenzen zur Laufzeit umzuwandeln, im Allgemeinen zum Zwecke des Castings eines Zeigers oder einer Referenz nach oben oder unten in einer Vererbungskette (Vererbungshierarchie).
dynamic_cast(ausdruck)
Der Zieltyp muss ein Zeiger- oder Verweistyp sein, und der Ausdruck muss zu einem Zeiger oder Verweis ausgewertet werden. 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 umgewandelten Ausdrucks ein Zeiger ist, wird NULL zurückgegeben. Wenn eine dynamische Umwandlung einer Referenz fehlschlägt, wird eine bad_cast-Ausnahme ausgelöst. Wenn dies nicht fehlschlägt, gibt die dynamische Umwandlung einen Zeiger oder eine Referenz des Zieltyps auf das Objekt zurück, auf das der Ausdruck verwiesen hat.
reinterpret_cast
Reinterpret cast wandelt einfach einen Typ bitweise in einen anderen um. Jeder Zeiger oder integrale Typ kann mit Reinterpret Cast in einen anderen umgewandelt werden, was leicht einen Missbrauch ermöglicht. Zum Beispiel könnte man mit reinterpret cast einen Integer-Zeiger auf unsichere Weise in einen String-Zeiger umwandeln.
Jason Baker
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.
Nehmen Sie diesen Rat an, was Sie wollen. Ich bin weit davon entfernt, ein C++-Guru zu sein.
ugasoft
Vermeiden Sie die Verwendung von C-Style-Casts.
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.
DrPizza
Umwandlungen im C-Stil führen const_cast, static_cast und reinterpret_cast zusammen.
Ich wünschte, C++ hätte keine Umwandlungen im C-Stil. C++-Casts heben sich richtig ab (wie sie es sollten; Casts weisen normalerweise auf etwas Schlechtes hin) und unterscheiden richtig zwischen den verschiedenen Arten von Konvertierungen, die Casts durchführen. Sie erlauben auch, ähnlich aussehende Funktionen zu schreiben, zB boost::lexical_cast, was aus Konsistenzsicht ganz nett ist.
dynamic_cast unterstützt nur Zeiger- und Referenztypen. Es kehrt zurück NULL wenn die Umwandlung nicht mö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 (Sie werden einfach mit einem ungültigen Wert enden).
Besetzungen im C-Stil (und andere) wurden in den anderen Antworten behandelt.
“Sie erhalten einfach einen ungültigen Wert” und ein undefiniertes Verhalten. Das heißt, das Programm hat sich falsch verhalten, auch wenn Sie den Wert nicht verwenden
– Neugieriger
3. November 2019 um 0:39 Uhr
10011800cookie-checkRegulärer Cast vs. static_cast vs. dynamic_cast [duplicate]yes
Ich würde die Umwandlung im alten C-Stil in C++ nicht als “regulären Cast” bezeichnen, da es alles andere als ist. Sie sollten C++ im Allgemeinen nicht verwenden, insbesondere bei Klassen, da es einfach zu leicht ist, Fehler damit zu machen. Die Verwendung davon ist ein Zeichen für einen C-Programmierer, der zu C++ gewechselt ist, aber C++ noch nicht ganz gelernt hat.
– Hyde
30. Januar 2013 um 7:03 Uhr
Wie kann eine Frage mit Antwort ein Duplikat einer Frage ohne Antwort sein? mehr noch, diese Frage wurde früher gestellt als das “Original”
– Wladp
21. Juni 2015 um 8:28 Uhr
@Vladp Falls Sie sich immer noch fragen oder jemand anderes dies liest und sich wundert. (Außerdem, fürs Protokoll, es war kein Moderator, der dies geschlossen hat, sondern ein Benutzer mit einem Dupe-Hammer)
– Nik
1. Februar 2017 um 1:16 Uhr
Zu Ihrer Information, die verknüpfte Frage hat viel mehr positive Stimmen und die Antworten haben auch viel mehr positive Stimmen. Auch die verknüpfte Frage enthält einige gute nicht-theoretische Beispiele. (Außerdem bezieht sich die verknüpfte Frage nicht fälschlicherweise auf die Typumwandlungssyntax im C-Stil als “reguläre Umwandlung”.)
– Trevor Boyd Smith
7. März 2018 um 14:20 Uhr