Was sind die richtigen Verwendungszwecke von:
static_cast
dynamic_cast
const_cast
reinterpret_cast
- C-Stil Guss
(type)value
- Guss im Funktionsstil
type(value)
Wie kann man entscheiden, welche in welchen Fällen verwendet werden sollen?
Was sind die richtigen Verwendungszwecke von:
static_cast
dynamic_cast
const_cast
reinterpret_cast
(type)value
type(value)
Wie kann man entscheiden, welche in welchen Fällen verwendet werden sollen?
static_cast
ist der erste Wurf, den Sie versuchen sollten. Es macht Dinge wie implizite Konvertierungen zwischen Typen (wie int
à float
oder Zeiger auf void*
), und sie kann auch explizite (oder implizite) Konvertierungsfunktionen aufrufen. In vielen Fällen ist die explizite Angabe von static_cast
ist nicht notwendig, aber es ist wichtig zu wissen, dass die T(something)
Syntax ist äquivalent zu (T)something
und sollte vermieden werden (mehr dazu später). A T(something, something_else)
ist jedoch sicher und wird garantiert den Konstruktor aufrufen.
static_cast
kann auch durch Vererbungshierarchien wirken. Beim Casting nach oben (in Richtung einer Basisklasse) ist es nicht notwendig, aber beim Casting nach unten kann es verwendet werden, solange es nicht durch virtual
Vererbung. Es findet jedoch keine Überprüfung statt, und es ist ein undefiniertes Verhalten, wenn static_cast
in einer Hierarchie zu einem Typ, der eigentlich nicht der Typ des Objekts ist.
const_cast
kann verwendet werden, um zu entfernen oder hinzuzufügen const
zu einer Variablen; kein anderer C++-Cast ist in der Lage, ihn zu entfernen (nicht einmal reinterpret_cast
). Es ist wichtig zu beachten, dass die Änderung eines früheren const
Wert ist nur dann undefiniert, wenn die ursprüngliche Variable const
wenn Sie damit die const
einen Verweis auf etwas aus, das nicht mit const
ist es sicher. Dies kann nützlich sein, wenn man Mitgliedsfunktionen überlädt, die auf const
zum Beispiel. Es kann auch verwendet werden, um hinzuzufügen const
zu einem Objekt, z. B. um eine Überladung einer Mitgliedsfunktion aufzurufen.
const_cast
funktioniert in ähnlicher Weise auch bei volatile
aber das ist weniger üblich.
dynamic_cast
wird ausschließlich für die Behandlung von Polymorphismus verwendet. Sie können einen Zeiger oder eine Referenz auf einen polymorphen Typ in einen beliebigen anderen Klassentyp umwandeln (ein polymorpher Typ hat mindestens eine virtuelle Funktion, die deklariert oder geerbt wurde). Sie können damit nicht nur nach unten casten, sondern auch seitwärts oder sogar in eine andere Kette. Die dynamic_cast
sucht das gewünschte Objekt und gibt es nach Möglichkeit zurück. Wenn das nicht möglich ist, gibt es zurück nullptr
im Falle eines Zeigers, oder werfen std::bad_cast
im Falle eines Verweises.
dynamic_cast
hat allerdings einige Einschränkungen. Es funktioniert nicht, wenn es mehrere Objekte desselben Typs in der Vererbungshierarchie gibt (der so genannte "gefürchtete Diamant") und Sie nicht mit virtual
Vererbung. Es kann auch nur durch die öffentliche Vererbung gehen - es wird immer scheitern, durch protected
o private
Vererbung. Dies ist jedoch selten ein Problem, da solche Formen der Vererbung selten sind.
reinterpret_cast
ist der gefährlichste Wurf und sollte nur sehr sparsam eingesetzt werden. Er wandelt einen Typ direkt in einen anderen um - wie z.B. das Casting des Wertes von einem Zeiger in einen anderen oder das Speichern eines Zeigers in einer int
oder alle möglichen anderen unangenehmen Dinge. Im Großen und Ganzen ist die einzige Garantie, die Sie mit reinterpret_cast
ist, dass Sie normalerweise genau denselben Wert erhalten, wenn Sie das Ergebnis auf den ursprünglichen Typ zurückführen (aber no wenn der Zwischentyp kleiner ist als der ursprüngliche Typ). Es gibt eine Reihe von Konvertierungen, die reinterpret_cast
auch nicht tun kann. Es wird hauptsächlich für besonders seltsame Konvertierungen und Bitmanipulationen verwendet, wie das Umwandeln eines Rohdatenstroms in tatsächliche Daten oder das Speichern von Daten in den niedrigen Bits eines Zeigers auf ausgerichtete Daten.
C-Stil Guss y Funktionsguss sind Würfe mit (type)object
o type(object)
und sind funktional gleichwertig. Sie sind definiert als der erste der folgenden Fälle, der erfolgreich ist:
const_cast
static_cast
(allerdings ohne Berücksichtigung der Zugangsbeschränkungen)static_cast
(siehe oben), dann const_cast
reinterpret_cast
reinterpret_cast
entonces const_cast
Sie kann daher in einigen Fällen als Ersatz für andere Würfe verwendet werden, kann aber extrem gefährlich sein, da sie sich in eine reinterpret_cast
und letzteres sollte bevorzugt werden, wenn ein explizites Casting erforderlich ist, es sei denn, Sie sind sich sicher static_cast
erfolgreich sein wird oder reinterpret_cast
wird scheitern. Selbst dann sollten Sie die längere, explizitere Option in Betracht ziehen.
C-artige Casts ignorieren auch die Zugriffskontrolle bei der Durchführung einer static_cast
Das bedeutet, dass sie eine Operation durchführen können, die keine andere Besetzung durchführen kann. Dies ist meist ein Trick, obwohl, und meiner Meinung nach ist nur ein weiterer Grund, um C-Stil Casts zu vermeiden.
Ich würde als erste Option dynamic_cast<> empfehlen, was natürlich nur wie oben beschrieben funktioniert. Wann immer Sie dynamic_cast<> verwenden können, wird geprüft, ob der Typ wirklich das ist, was Sie glauben.
Dynamic_cast ist nur für polymorphe Typen. Sie brauchen es nur zu verwenden, wenn Sie auf eine abgeleitete Klasse casten. static_cast ist sicherlich die erste Option, es sei denn, Sie benötigen speziell die Funktionalität von dynamic_cast. Es ist keine wundersame Wunderwaffe "type-checking cast" im Allgemeinen.
Der Abschnitt über reinterpret_cast zeigt eine Aliasing-Verletzung und damit undefiniertes Verhalten. Wenn Sie diese Art von Typkonvertierung wünschen, ist memcpy der bessere Weg.
Utilice dynamic_cast
zur Umwandlung von Zeigern/Referenzen innerhalb einer Vererbungshierarchie.
Utilice static_cast
für gewöhnliche Typkonvertierungen.
Utilice reinterpret_cast
für die Neuinterpretation von Bitmustern auf niedriger Ebene. Mit äußerster Vorsicht zu verwenden.
Utilice const_cast
zum Wegwerfen const/volatile
. Vermeiden Sie dies, es sei denn, Sie sind mit einem const-inkorrekte API stecken.
Seien Sie vorsichtig mit dynamic_cast. Es verlässt sich auf RTTI und dies wird nicht wie erwartet über die Grenzen von Shared Libraries hinweg funktionieren. Da Sie die ausführbare Datei und die Shared Library unabhängig voneinander erstellen, gibt es keine standardisierte Möglichkeit, RTTI über verschiedene Builds hinweg zu synchronisieren. Aus diesem Grund gibt es in der Qt-Bibliothek qobject_cast<>, das die QObject-Typinformationen zur Überprüfung der Typen verwendet.
(Viele theoretische und konzeptionelle Erläuterungen wurden bereits gegeben)
Nachstehend sind einige der praktische Beispiele als ich noch statisch_gecastet , dynamisch_gecastet , const_cast , uminterpretieren_gießen .
(Siehe auch dies, um die Erläuterung zu verstehen: http://www.cplusplus.com/doc/tutorial/typecasting/ )
static_cast :
OnEventData(void* pData)
{
......
// pData is a void* pData,
// EventData is a structure e.g.
// typedef struct _EventData {
// std::string id;
// std:: string remote_id;
// } EventData;
// On Some Situation a void pointer *pData
// has been static_casted as
// EventData* pointer
EventData *evtdata = static_cast<EventData*>(pData);
.....
}
dynamic_cast :
void DebugLog::OnMessage(Message *msg)
{
static DebugMsgData *debug;
static XYZMsgData *xyz;
if(debug = dynamic_cast<DebugMsgData*>(msg->pdata)){
// debug message
}
else if(xyz = dynamic_cast<XYZMsgData*>(msg->pdata)){
// xyz message
}
else/* if( ... )*/{
// ...
}
}
const_cast :
// *Passwd declared as a const
const unsigned char *Passwd
// on some situation it require to remove its constness
const_cast<unsigned char*>(Passwd)
reinterpret_cast :
typedef unsigned short uint16;
// Read Bytes returns that 2 bytes got read.
bool ByteBuffer::ReadUInt16(uint16& val) {
return ReadBytes(reinterpret_cast<char*>(&val), 2);
}
Die Theorie einiger der anderen Antworten ist gut, aber immer noch verwirrend. Wenn man diese Beispiele sieht, nachdem man die anderen Antworten gelesen hat, macht das alles wirklich Sinn. Das heißt, ohne die Beispiele war ich immer noch unsicher, aber mit ihnen bin ich mir jetzt sicher, was die anderen Antworten bedeuten.
Zur letzten Verwendung von reinterpret_cast: Ist dies nicht dasselbe wie die Verwendung von static_cast<char*>(&val)
?
@LorenzoBelli Natürlich nicht. Haben Sie es versucht? Letzteres ist kein gültiges C++ und blockiert die Kompilierung. static_cast
funktioniert nur zwischen Typen mit definierten Konvertierungen, sichtbarer Beziehung durch Vererbung oder nach/von void *
. Für alles andere gibt es andere Gussformen. reinterpret cast
zu jeder char *
ist erlaubt, um das Lesen der Darstellung eines beliebigen Objekts zu ermöglichen - und einer der einzigen Fälle, in denen dieses Schlüsselwort nützlich ist und nicht ein zügelloser Generator von Implementierungs-/undefiniertem Verhalten. Dies wird jedoch nicht als "normale" Konvertierung betrachtet und ist daher nicht durch die (normalerweise) sehr konservativen static_cast
.
Es könnte helfen, wenn Sie ein wenig über Interna wissen...
statisch_gecastet
float
à int
. Verwenden Sie static_cast
für sie.A
à B
, static_cast
ruft auf. B
Der Konstruktor von A
als Param. Alternativ dazu, A
könnte einen Konvertierungsoperator haben (d.h. A::operator B()
). Wenn B
keinen solchen Konstruktor hat, oder A
keinen Konvertierungsoperator hat, erhalten Sie einen Kompilierzeitfehler.A*
à B*
ist immer erfolgreich, wenn sich A und B in der Vererbungshierarchie befinden (oder ungültig sind), andernfalls erhalten Sie einen Kompilierfehler.A&
à B&
.dynamisch_gecastet
(Base*)
à (Derived*)
kann fehlschlagen, wenn der Zeiger nicht tatsächlich von einem abgeleiteten Typ ist.A*
à B*
Wenn cast ungültig ist, wird dynamic_cast nullptr zurückgeben.A&
à B&
wenn cast ungültig ist, löst dynamic_cast die bad_cast-Ausnahme aus.const_cast
set<T>
die nur ihre Elemente als const zurückgibt, um sicherzustellen, dass Sie den Schlüssel nicht ändern. Wenn Sie jedoch die Absicht haben, die Nicht-Schlüssel-Mitglieder des Objekts zu ändern, sollte dies in Ordnung sein. Sie können const_cast verwenden, um constness zu entfernen.T& SomeClass::foo()
wie auch const T& SomeClass::foo() const
. Um doppelten Code zu vermeiden, können Sie const_cast anwenden, um den Wert einer Funktion aus einer anderen zurückzugeben.uminterpretieren_gießen
float
auf 4 Bytes von int
um zu sehen, wie Bits in float
sieht so aus.
Ich habe die Informationen zu den Konvertierungsoperatoren hinzugefügt, aber es gibt noch ein paar andere Dinge, die ebenfalls behoben werden sollten, und ich fühle mich nicht wohl dabei, dies zu sehr zu aktualisieren. Die Punkte sind: 1. If you cast base pointer to derived pointer but if actual object is not really derived type then you don't get error. You get bad pointer and segfault at runtime.
Sie erhalten UB, was zu einem Segfault zur Laufzeit führen kann, wenn Sie Glück haben. 2. Dynamische Würfe können auch in Cross-Casting verwendet werden. 3. Const Casts können in einigen Fällen zu UB führen. Verwendung von mutable
kann eine bessere Wahl sein, um logische Konstanten zu implementieren.
@Adrian Sie haben in jeder Hinsicht Recht. Die Antwort ist für Leute auf mehr oder weniger Anfängerniveau geschrieben und ich wollte sie nicht mit allen anderen Komplikationen überwältigen, die damit einhergehen mutable
, Kreuzguß usw.
@Shital Shah "Cast von Derived zu Base oder umgekehrt erzeugt eine neue Kopie! Für Leute, die aus C#/Java kommen, kann dies eine große Überraschung sein, weil das Ergebnis im Grunde ein abgehacktes Objekt ist, das aus Derived erstellt wurde." Könnten Sie bitte einen einfachen Beispielcode zeigen, um es leichter zu verstehen? Vielen Dank!
static_cast
gegen dynamic_cast
gegen reinterpret_cast
Interna zu einem Downcast/Upcast
In dieser Antwort möchte ich diese drei Mechanismen anhand eines konkreten Upcast/Downcast-Beispiels vergleichen und analysieren, was mit den zugrundeliegenden Zeigern/Speicher/Assemblierung geschieht, um ein konkretes Verständnis dafür zu vermitteln, wie sie sich vergleichen.
Ich glaube, dass dies ein gutes Gespür dafür vermittelt, wie unterschiedlich diese Formen sind:
static_cast
: macht einen Adress-Offset zur Laufzeit (geringe Laufzeitauswirkung) und keine Sicherheitsüberprüfung, ob ein Downcast korrekt ist.
dyanamic_cast
: macht zur Laufzeit den gleichen Adressoffset wie static_cast
aber auch eine teure Sicherheitsprüfung, ob ein Downcast mit RTTI korrekt ist.
Mit dieser Sicherheitsprüfung können Sie abfragen, ob ein Basisklassenzeiger zur Laufzeit von einem bestimmten Typ ist, indem Sie eine Rückgabe von nullptr
was auf einen ungültigen Downcast hinweist.
Wenn Ihr Code also nicht in der Lage ist, das zu überprüfen nullptr
und eine gültige Nicht-Abbruch-Aktion durchführen, sollten Sie einfach static_cast
anstelle der dynamischen Besetzung.
Wenn ein Abbruch die einzige Aktion ist, die Ihr Code ausführen kann, möchten Sie vielleicht nur die Option dynamic_cast
in Debug-Builds ( -NDEBUG
), und verwenden Sie static_cast
sonst z.B. wie hier geschehen , um Ihre schnellen Läufe nicht zu verlangsamen.
reinterpret_cast
: macht zur Laufzeit nichts, nicht einmal den Adressoffset. Der Zeiger muss genau auf den richtigen Typ zeigen, nicht einmal eine Basisklasse funktioniert. Im Allgemeinen wollen Sie das nicht, es sei denn, es handelt sich um rohe Byte-Streams.
Betrachten Sie das folgende Codebeispiel:
main.cpp
#include <iostream>
struct B1 {
B1(int int_in_b1) : int_in_b1(int_in_b1) {}
virtual ~B1() {}
void f0() {}
virtual int f1() { return 1; }
int int_in_b1;
};
struct B2 {
B2(int int_in_b2) : int_in_b2(int_in_b2) {}
virtual ~B2() {}
virtual int f2() { return 2; }
int int_in_b2;
};
struct D : public B1, public B2 {
D(int int_in_b1, int int_in_b2, int int_in_d)
: B1(int_in_b1), B2(int_in_b2), int_in_d(int_in_d) {}
void d() {}
int f2() { return 3; }
int int_in_d;
};
int main() {
B2 *b2s[2];
B2 b2{11};
D *dp;
D d{1, 2, 3};
// The memory layout must support the virtual method call use case.
b2s[0] = &b2;
// An upcast is an implicit static_cast<>().
b2s[1] = &d;
std::cout << "&d " << &d << std::endl;
std::cout << "b2s[0] " << b2s[0] << std::endl;
std::cout << "b2s[1] " << b2s[1] << std::endl;
std::cout << "b2s[0]->f2() " << b2s[0]->f2() << std::endl;
std::cout << "b2s[1]->f2() " << b2s[1]->f2() << std::endl;
// Now for some downcasts.
// Cannot be done implicitly
// error: invalid conversion from ‘B2*’ to ‘D*’ [-fpermissive]
// dp = (b2s[0]);
// Undefined behaviour to an unrelated memory address because this is a B2, not D.
dp = static_cast<D*>(b2s[0]);
std::cout << "static_cast<D*>(b2s[0]) " << dp << std::endl;
std::cout << "static_cast<D*>(b2s[0])->int_in_d " << dp->int_in_d << std::endl;
// OK
dp = static_cast<D*>(b2s[1]);
std::cout << "static_cast<D*>(b2s[1]) " << dp << std::endl;
std::cout << "static_cast<D*>(b2s[1])->int_in_d " << dp->int_in_d << std::endl;
// Segfault because dp is nullptr.
dp = dynamic_cast<D*>(b2s[0]);
std::cout << "dynamic_cast<D*>(b2s[0]) " << dp << std::endl;
//std::cout << "dynamic_cast<D*>(b2s[0])->int_in_d " << dp->int_in_d << std::endl;
// OK
dp = dynamic_cast<D*>(b2s[1]);
std::cout << "dynamic_cast<D*>(b2s[1]) " << dp << std::endl;
std::cout << "dynamic_cast<D*>(b2s[1])->int_in_d " << dp->int_in_d << std::endl;
// Undefined behaviour to an unrelated memory address because this
// did not calculate the offset to get from B2* to D*.
dp = reinterpret_cast<D*>(b2s[1]);
std::cout << "reinterpret_cast<D*>(b2s[1]) " << dp << std::endl;
std::cout << "reinterpret_cast<D*>(b2s[1])->int_in_d " << dp->int_in_d << std::endl;
}
Kompilieren, ausführen und disassemblieren mit:
g++ -ggdb3 -O0 -std=c++11 -Wall -Wextra -pedantic -o main.out main.cpp
setarch `uname -m` -R ./main.out
gdb -batch -ex "disassemble/rs main" main.out
donde setarch
es verwendet, um ASLR zu deaktivieren um Läufe besser vergleichen zu können.
Mögliche Ausgabe:
&d 0x7fffffffc930
b2s[0] 0x7fffffffc920
b2s[1] 0x7fffffffc940
b2s[0]->f2() 2
b2s[1]->f2() 3
static_cast<D*>(b2s[0]) 0x7fffffffc910
static_cast<D*>(b2s[0])->int_in_d 1
static_cast<D*>(b2s[1]) 0x7fffffffc930
static_cast<D*>(b2s[1])->int_in_d 3
dynamic_cast<D*>(b2s[0]) 0
dynamic_cast<D*>(b2s[1]) 0x7fffffffc930
dynamic_cast<D*>(b2s[1])->int_in_d 3
reinterpret_cast<D*>(b2s[1]) 0x7fffffffc940
reinterpret_cast<D*>(b2s[1])->int_in_d 32767
Nun, wie erwähnt bei: https://en.wikipedia.org/wiki/Virtual_method_table um die virtuellen Methodenaufrufe effizient zu unterstützen, wobei angenommen wird, dass die Speicherdatenstrukturen von B1 von der Form sind:
B1:
+0: pointer to virtual method table of B1
+4: value of int_in_b1
et B2
ist von der Form her:
B2:
+0: pointer to virtual method table of B2
+4: value of int_in_b2
dann Speicherdatenstruktur von D
muss in etwa so aussehen:
D:
+0: pointer to virtual method table of D (for B1)
+4: value of int_in_b1
+8: pointer to virtual method table of D (for B2)
+12: value of int_in_b2
+16: value of int_in_d
Die wichtigste Tatsache ist, dass die Speicherdatenstruktur von D
enthält in seinem Inneren eine Speicherstruktur, die mit der von B1
y B2
d.h.:
int_in_b1
int_in_b2
Deshalb kommen wir zu der kritischen Schlussfolgerung:
ein Upcast oder Downcast muss den Zeigerwert nur um einen zur Kompilierzeit bekannten Wert verschieben
Auf diese Weise kann, wenn D
an das Array des Basistyps übergeben wird, berechnet der Typ-Cast tatsächlich diesen Offset und zeigt etwas, das genau wie ein gültiger B2
im Speicher, mit der Ausnahme, dass diese die vtable für D
anstelle von B2
und daher funktionieren alle virtuellen Anrufe auf transparente Weise.
Z.B.:
b2s[1] = &d;
muss lediglich die Adresse von d
+ 8, um die entsprechende B2-ähnliche Datenstruktur zu erreichen.
Nun können wir uns endlich wieder dem Type Casting und der Analyse unseres konkreten Beispiels zuwenden.
In der stdout-Ausgabe sehen wir:
&d 0x7fffffffc930
b2s[1] 0x7fffffffc940
Daher ist die implizite static_cast
Die dort vorgenommene Berechnung des Offsets von der vollen D
Datenstruktur an 0x7fffffffc930 in die B2
wie zum Beispiel 0x7fffffffc940. Wir folgern auch, dass das, was zwischen 0x7fffffffc930 und 0x7fffffffc940 liegt, wahrscheinlich die B1
Daten und vtable.
Auf den nach unten gerichteten Abschnitten ist es nun einfach zu verstehen, wie und warum die ungültigen Abschnitte scheitern:
static_cast<D*>(b2s[0]) 0x7fffffffc910
: Der Compiler hat gerade 0x10 Bytes zur Kompilierzeit erhöht, um von einer B2
zu dem enthaltenen D
Aber weil b2s[0]
war kein D
verweist er nun auf einen undefinierten Speicherbereich.
Die Demontage ist:
49 dp = static_cast<D*>(b2s[0]);
0x0000000000000fc8 <+414>: 48 8b 45 d0 mov -0x30(%rbp),%rax
0x0000000000000fcc <+418>: 48 85 c0 test %rax,%rax
0x0000000000000fcf <+421>: 74 0a je 0xfdb <main()+433>
0x0000000000000fd1 <+423>: 48 8b 45 d0 mov -0x30(%rbp),%rax
0x0000000000000fd5 <+427>: 48 83 e8 10 sub $0x10,%rax
0x0000000000000fd9 <+431>: eb 05 jmp 0xfe0 <main()+438>
0x0000000000000fdb <+433>: b8 00 00 00 00 mov $0x0,%eax
0x0000000000000fe0 <+438>: 48 89 45 98 mov %rax,-0x68(%rbp)
Wir sehen also, dass der GCC dies tut:
D
die es nicht gibtdynamic_cast<D*>(b2s[0]) 0
: C++ stellte fest, dass der Cast ungültig war und gab nullptr
!
Es gibt keine Möglichkeit, dies zur Kompilierzeit zu tun, und wir werden dies anhand der Disassemblierung bestätigen:
59 dp = dynamic_cast<D*>(b2s[0]);
0x00000000000010ec <+706>: 48 8b 45 d0 mov -0x30(%rbp),%rax
0x00000000000010f0 <+710>: 48 85 c0 test %rax,%rax
0x00000000000010f3 <+713>: 74 1d je 0x1112 <main()+744>
0x00000000000010f5 <+715>: b9 10 00 00 00 mov $0x10,%ecx
0x00000000000010fa <+720>: 48 8d 15 f7 0b 20 00 lea 0x200bf7(%rip),%rdx # 0x201cf8 <_ZTI1D>
0x0000000000001101 <+727>: 48 8d 35 28 0c 20 00 lea 0x200c28(%rip),%rsi # 0x201d30 <_ZTI2B2>
0x0000000000001108 <+734>: 48 89 c7 mov %rax,%rdi
0x000000000000110b <+737>: e8 c0 fb ff ff callq 0xcd0 <__dynamic_cast@plt>
0x0000000000001110 <+742>: eb 05 jmp 0x1117 <main()+749>
0x0000000000001112 <+744>: b8 00 00 00 00 mov $0x0,%eax
0x0000000000001117 <+749>: 48 89 45 98 mov %rax,-0x68(%rbp)
Zuerst wird eine NULL-Prüfung durchgeführt, und es wird NULL zurückgegeben, wenn die Eingabe NULL ist.
Andernfalls werden einige Argumente in RDX, RSI und RDI gesetzt und die __dynamic_cast
.
Ich habe jetzt nicht die Geduld, das weiter zu analysieren, aber wie andere schon sagten, kann das nur funktionieren, wenn __dynamic_cast
um auf einige zusätzliche RTTI-Speicherdatenstrukturen zuzugreifen, die die Klassenhierarchie darstellen.
Sie muss daher von der B2
Eintrag für diese Tabelle und durchläuft dann diese Klassenhierarchie, bis es feststellt, dass die vtable für eine D
abgetippt b2s[0]
.
Aus diesem Grund ist der dynamische Guss potenziell teuer! Hier ist ein Beispiel, bei dem ein One-Liner-Patch eine dynamic_cast
zu einer static_cast
in einem komplexen Projekt die Laufzeit um 33% reduziert! .
reinterpret_cast<D*>(b2s[1]) 0x7fffffffc940
Dieser glaubt uns einfach blind: Wir haben gesagt, es gibt eine D
unter der Adresse b2s[1]
und der Compiler führt keine Offset-Berechnungen durch.
Aber das ist falsch, denn D befindet sich tatsächlich an 0x7fffffffc930, was an 0x7fffffffc940 liegt, ist die B2-ähnliche Struktur innerhalb von D! Es wird also auf Müll zugegriffen.
Wir können dies anhand der horrenden -O0
Baugruppe, die den Wert nur verschiebt:
70 dp = reinterpret_cast<D*>(b2s[1]);
0x00000000000011fa <+976>: 48 8b 45 d8 mov -0x28(%rbp),%rax
0x00000000000011fe <+980>: 48 89 45 98 mov %rax,-0x68(%rbp)
Verwandte Fragen:
Getestet auf Ubuntu 18.04 amd64, GCC 7.4.0.
CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.
46 Stimmen
Vielleicht eine gute Referenz hier: Wie erklären Sie einem neuen C++-Programmierer die Unterschiede zwischen static_cast, reinterpret_cast, const_cast und dynamic_cast? .
4 Stimmen
Einige nützliche konkrete Beispiele für die Verwendung verschiedener Arten von Gussformen finden Sie in der ersten Antwort auf eine ähnliche Frage in dieses andere Thema .
5 Stimmen
Sie können oben wirklich gute Antworten auf Ihre Frage finden. Aber ich möchte hier noch einen weiteren Punkt anbringen, @e.James "Es gibt nichts, was diese neuen C++ Cast-Operatoren tun können und C Style Cast nicht. Sie sind mehr oder weniger für die bessere Lesbarkeit des Codes hinzugefügt worden."
4 Stimmen
@BreakBadSP Die neuen Darsteller sind no nur zur besseren Lesbarkeit des Codes. Sie sind dazu da, um es schwieriger zu machen, gefährliche Dinge zu tun, wie z.B. const weg zu casten oder Zeiger anstelle ihrer Werte zu casten. static_cast hat viel weniger Möglichkeiten, etwas Gefährliches zu tun, als ein c style cast!
0 Stimmen
@FourtyTwo einverstanden