Beide static_cast
und reinterpret_cast
scheinen für das Casting gut zu funktionieren void*
zu einem anderen Zeigertyp. Gibt es einen guten Grund, das eine dem anderen vorzuziehen?
Soll ich static_cast oder reinterpret_cast verwenden, wenn ich eine void* in was auch immer umwandele
Andy
Konrad Rudolf
Verwenden static_cast
: es ist der engste Guss, der genau beschreibt, was hier umgebaut wird.
Es gibt ein Missverständnis, dass mit reinterpret_cast
wäre eine bessere Übereinstimmung, weil es bedeutet, „Type Safety komplett zu ignorieren und einfach von A nach B zu casten“.
Dies beschreibt jedoch nicht wirklich die Wirkung von a reinterpret_cast
. Eher, reinterpret_cast
hat eine Reihe von Bedeutungen, für die alle gilt, dass „die Abbildung durchgeführt von reinterpret_cast
ist implementierungsdefiniert.“ [5.2.10.3]
Aber im speziellen Fall des Gießens ab void*
zu T*
das Mapping ist durch den Standard vollkommen wohldefiniert; nämlich einem typlosen Zeiger einen Typ zuzuweisen, ohne seine Adresse zu ändern.
Dies ist ein Grund zu bevorzugen static_cast
.
Zusätzlich und wohl noch wichtiger ist die Tatsache, dass jede Verwendung von reinterpret_cast
ist geradezu gefährlich, weil es wirklich alles in etwas anderes umwandelt (für Zeiger), während static_cast
ist viel restriktiver und bietet somit ein besseres Schutzniveau. Dies hat mich bereits vor Fehlern bewahrt, bei denen ich versehentlich versucht habe, einen Zeigertyp in einen anderen zu zwingen.
Die static_cast
eignet sich besser zum Konvertieren von a void*
zu einem Zeiger eines anderen Typs.
static_cast
ist die Besetzung der Wahl, wenn es eine natürliche, intuitive Konvertierung zwischen zwei Typen gibt, die nicht unbedingt zur Laufzeit funktioniert. Sie können zum Beispiel verwenden static_cast
Basisklassenzeiger in abgeleitete Klassenzeiger zu konvertieren, was in manchen Fällen sinnvoll ist, aber erst zur Laufzeit verifiziert werden kann. Ebenso können Sie verwenden static_cast
umwandeln von einem int
zu einem char
die gut definiert ist, aber bei der Ausführung zu einem Genauigkeitsverlust führen kann.
reinterpret_cast
, auf der anderen Seite, ist ein Casting-Operator, der für Konvertierungen entwickelt wurde, die grundsätzlich nicht sicher oder nicht portabel sind. Sie können zum Beispiel verwenden reinterpret_cast
umwandeln von a void *
zu einem int
das korrekt funktioniert, wenn Ihr System zufällig eine hat sizeof (void*)
≤ sizeof (int)
. Sie können auch verwenden reinterpret_cast
umwandeln a float*
zu einem int*
oder umgekehrt, was plattformspezifisch ist, da die jeweiligen Darstellungen von float
s und int
s haben nicht garantiert etwas miteinander gemeinsam.
Kurz gesagt, wenn Sie jemals eine Konvertierung durchführen, bei der die Umwandlung logisch sinnvoll ist, aber zur Laufzeit möglicherweise nicht unbedingt erfolgreich ist, vermeiden Sie dies reinterpret_cast
. static_cast
ist eine gute Wahl, wenn Sie etwas Vorwissen darüber haben, dass die Umwandlung zur Laufzeit funktionieren wird, und dem Compiler mitteilen: “Ich weiß, dass dies möglicherweise nicht funktioniert, aber es macht zumindest Sinn und ich habe Grund zu der Annahme, dass es richtig funktioniert zur Laufzeit das Richtige tun.” Der Compiler kann dann überprüfen, ob die Umwandlung zwischen verwandten Typen erfolgt, und einen Kompilierzeitfehler melden, wenn dies nicht der Fall ist. Verwenden reinterpret_cast
Um dies mit Zeigerkonvertierungen zu tun, wird die Sicherheitsüberprüfung zur Kompilierzeit vollständig umgangen.
Es gibt einige Situationen, in denen Sie möglicherweise a verwenden möchten dynamic_cast
anstelle einer static_cast
aber diese beinhalten meistens Besetzungen in einer Klassenhierarchie und betreffen (nur selten) direkt void*
.
Welches von der Spezifikation bevorzugt wird, wird nicht übermäßig als “das Richtige” erwähnt (oder zumindest erinnere ich mich nicht, dass einer von ihnen so erwähnt wurde). Ich denke jedoch, dass die Spezifikation es möchte verwenden static_cast
über reinterpret_cast
. Wenn Sie beispielsweise eine Umwandlung im C-Stil verwenden, wie in
A* ptr = (A*) myVoidPointer;
Die erprobte Reihenfolge der Casting-Operatoren versucht immer, a zu verwenden static_cast
vor einem reinterpret_cast
was das gewünschte Verhalten ist reinterpret_cast
ist nicht garantiert tragbar.
-
Zur Verdeutlichung: was der Autor hier mit „
static_cast
… funktioniert nicht unbedingt zur Laufzeit” ist: “Ihr Programm kann später abstürzen.” Wenn Siestatic_cast
von einem Basistyp zu einem abgeleiteten Typ, it Wille zur Laufzeit “arbeiten” (dh Sie werden nicht eine Ausnahme bekommen oder aNULL
Zeiger), aber das Ergebnis kann auf den falschen Speicherort zeigen, wenn Mehrfachvererbung beteiligt ist. (Siehe diese Antwort für weitere Details.) Nurdynamic_cast
führt eine Laufzeitprüfung durch (unter Verwendung von RTTI) und schlägt ordnungsgemäß fehl, wenn die Umwandlung ungültig ist.– andrewtc
9. August 2014 um 22:51 Uhr
Das ist eine schwierige Frage. Auf der einen Seite macht Konrad einen hervorragenden Punkt in Bezug auf die Spezifikationsdefinition für reinterpret_cast, obwohl es in der Praxis wahrscheinlich dasselbe tut. Auf der anderen Seite, wenn Sie zwischen Zeigertypen umwandeln (wie es zum Beispiel bei der Indizierung im Speicher über ein char * ziemlich üblich ist), static_cast wird einen Compiler-Fehler generieren und Sie werden zur Verwendung gezwungen reinterpret_cast ohnehin.
In der Praxis benutze ich reinterpret_cast weil es die Absicht des Cast-Vorgangs besser beschreibt. Sie könnten sicherlich für einen anderen Operator plädieren, um nur Zeiger neu zu interpretieren (was garantiert, dass dieselbe Adresse zurückgegeben wird), aber es gibt keinen im Standard.
-
“anderer Operator, um nur den Zeiger neu zu interpretieren (was garantiert, dass dieselbe Adresse zurückgegeben wird)“Umarmung? Dieser Operator ist
reinterpret_cast
!– Neugieriger
19. Dezember 2011 um 6:18 Uhr
-
@curiousguy Nicht wahr nach dem Standard. reinterpret_cast garantiert NICHT, dass dieselbe Adresse verwendet wird. Nur das, wenn Sie_cast von einem Typ in einen anderen uminterpretieren und dann wieder zurückerhalten Sie dieselbe Adresse zurück, mit der Sie begonnen haben.
– ClydeTheGhost
1. März 2019 um 20:58 Uhr
Das hast du wahrscheinlich bekommen void*
mit impliziter Konvertierung, also sollten Sie verwenden static_cast
weil es der impliziten Konvertierung am nächsten kommt.
Verwenden static_cast
dafür. Nur in den seltensten Fällen, wenn keine andere Möglichkeit besteht reinterpret_cast
.
anton_rh
Casting hin und her void*
verwenden static_cast
und verwenden reinterpret_cast
ist identisch. Siehe die Antwort unter dem Link. Aber normalerweise static_cast
wird bevorzugt, weil es eine engere und im Allgemeinen (aber nicht in diesem speziellen Fall) sicherere Konvertierung ist.
Tim Diekmann
Ich schlage vor, immer die schwächstmögliche Besetzung zu verwenden.
reinterpret_cast
kann verwendet werden, um einen Zeiger auf a umzuwandeln float
. Je strukturbrechender der Gips ist, desto mehr Aufmerksamkeit erfordert seine Anwendung.
Im Falle von char*
würde ich C-Style-Cast verwenden, bis wir welche haben reinterpret_pointer_cast
weil es schwächer ist und nichts anderes ausreicht.
-
“reinterpret_cast kann verwendet werden, um einen Zeiger auf einen Float umzuwandeln.” Sicherlich nicht!
– Neugieriger
19. Dezember 2011 um 6:16 Uhr
-
Wahrscheinlich
float f = *reinterpret_cast<const float*>(&p);
– Ben Voigt
7. August 2013 um 15:33 Uhr
-
@BenVoigt Das ist Casting zwischen Zeigern; Einer von ihnen war zufällig ein Float-Zeiger.
– nodakai
1. Juni 2016 um 15:46 Uhr
-
@BenVoigt der “gesamte Ausdruck” ist jedoch keine Besetzung. Der Ausdruck besteht aus einer Dereferenzierung, die auf eine Umwandlung angewendet wird. Sie haben behauptet, dass es möglich sei, einen Zeiger darauf zu werfen
float
, was falsch ist. Der Ausdruck wird umgewandeltvoid **
zuconst float *
und verwendet dann eine Dereferenzierungsoperation (die KEINE Umwandlung ist), um zu konvertierenconst float *
zufloat
.– MM
27. Juli 2018 um 4:37 Uhr
-
@BenVoigt Sie haben diesen Code als Antwort auf die Frage von jemandem angeboten: “Wie wirke ich …”, und als dann jemand sagte, dass der Code zwischen Zeigern wirft (was er tut), sagten Sie “Nein”.
– MM
27. Juli 2018 um 4:54 Uhr
@anon Anscheinend hast du vorher noch nie mit POSIX-Threads gearbeitet.
– Benutzer470379
23. Dezember 2010 um 20:00 Uhr
@ user470379 Wow … das ist genau der Grund, warum ich bei SO auf diese Frage gelandet bin! Hervorragende Beobachtung :-).
– Oger Psalm33
21. Juni 2011 um 14:13 Uhr