Quali sono gli usi corretti di:
static_castdynamic_castconst_castreinterpret_cast- Cast in stile C.
(type)value - Cast di stile funzionale
type(value)
Come si decide quale utilizzare in quali casi specifici?
Quali sono gli usi corretti di:
static_castdynamic_castconst_castreinterpret_cast(type)valuetype(value)Come si decide quale utilizzare in quali casi specifici?
Risposte:
static_castè il primo cast che dovresti provare a usare. Fa cose come conversioni implicite tra tipi (come intto floato pointer to void*) e può anche chiamare funzioni di conversione esplicite (o implicite). In molti casi, static_castnon è necessario dichiarare esplicitamente , ma è importante notare che la T(something)sintassi è equivalente (T)somethinge dovrebbe essere evitata (ne parleremo più avanti). A T(something, something_else)è sicuro, tuttavia, e garantito per chiamare il costruttore.
static_castpuò anche eseguire il cast delle gerarchie ereditarie. Non è necessario quando si lancia verso l'alto (verso una classe base), ma quando si lancia verso il basso può essere utilizzato purché non passi attraverso l' virtualeredità. Tuttavia, non esegue il controllo ed è un comportamento indefinito static_castridurre una gerarchia a un tipo che non è effettivamente il tipo di oggetto.
const_castpuò essere usato per rimuovere o aggiungere consta una variabile; nessun altro cast C ++ è in grado di rimuoverlo (nemmeno reinterpret_cast). È importante notare che la modifica di un constvalore precedente non è definita solo se la variabile originale è const; se lo usi per togliere constun riferimento a qualcosa con cui non è stato dichiarato const, è sicuro. Ciò può essere utile quando si sovraccaricano le funzioni dei membri basate const, ad esempio. Può anche essere utilizzato per aggiungere consta un oggetto, ad esempio per chiamare un sovraccarico della funzione membro.
const_castfunziona anche allo stesso modo volatile, sebbene sia meno comune.
dynamic_castè utilizzato esclusivamente per la gestione del polimorfismo. È possibile eseguire il cast di un puntatore o riferimento a qualsiasi tipo polimorfico a qualsiasi altro tipo di classe (un tipo polimorfico ha almeno una funzione virtuale, dichiarata o ereditata). Puoi usarlo per più di un semplice lancio verso il basso: puoi lanciare lateralmente o anche su un'altra catena. Il dynamic_castcercherà l'oggetto desiderato e tornare, se possibile. In caso contrario, verrà restituito nullptrnel caso di un puntatore, oppure verrà lanciato std::bad_castnel caso di un riferimento.
dynamic_castha alcune limitazioni, però. Non funziona se ci sono più oggetti dello stesso tipo nella gerarchia dell'ereditarietà (il cosiddetto "diamante temuto") e non stai usando l' virtualereditarietà. Inoltre può solo passare attraverso l'eredità pubblica - non riuscirà sempre a viaggiare attraverso protectedo privateeredità. Questo è raramente un problema, tuttavia, poiché tali forme di eredità sono rare.
reinterpret_castè il cast più pericoloso e dovrebbe essere usato con parsimonia. Trasforma un tipo direttamente in un altro, come ad esempio lanciare il valore da un puntatore a un altro o archiviare un puntatore in una into altre cose brutte. In gran parte, l'unica garanzia che ottieni reinterpret_castè che normalmente se restituisci il risultato al tipo originale, otterrai lo stesso valore esatto (ma non se il tipo intermedio è più piccolo del tipo originale). Esistono anche numerose conversioni che reinterpret_castnon possono essere eseguite. Viene utilizzato principalmente per conversioni e manipolazioni di bit particolarmente strane, come trasformare un flusso di dati non elaborati in dati reali o archiviare i dati nei bit bassi di un puntatore in dati allineati.
Cast di tipo C e cast di tipo funzione sono calchi utilizzando (type)objecto type(object), rispettivamente, e sono funzionalmente equivalenti. Sono definiti come il primo dei seguenti che ha esito positivo:
const_caststatic_cast (sebbene ignorando le restrizioni di accesso)static_cast (vedi sopra), quindi const_castreinterpret_castreinterpret_cast, poi const_castPuò quindi essere usato come sostituto di altri cast in alcuni casi, ma può essere estremamente pericoloso a causa della capacità di devolvere in un reinterpret_cast, e quest'ultimo dovrebbe essere preferito quando è necessario il cast esplicito, a meno che non si sia sicuri che static_castavrà successo o reinterpret_castfallirà . Anche allora, considera l'opzione più lunga ed esplicita.
I lanci di tipo C ignorano anche il controllo di accesso quando eseguono un static_cast, il che significa che hanno la capacità di eseguire un'operazione che nessun altro cast può fare. Questo è principalmente un problema, e nella mia mente è solo un altro motivo per evitare i cast in stile C.
const(nemmeno reinterpret_cast)" ... davvero? Che dire reinterpret_cast<int *>(reinterpret_cast<uintptr_t>(static_cast<int const *>(0)))?
reinterpret_castè spesso l'arma preferita quando si ha a che fare con una serie di tipi di dati opachi di un'API
Utilizzare dynamic_castper convertire puntatori / riferimenti all'interno di una gerarchia di ereditarietà.
Utilizzare static_castper conversioni di tipo ordinario.
Utilizzare reinterpret_castper la reinterpretazione a basso livello di modelli di bit. Utilizzare con estrema cautela.
Utilizzare const_castper gettare via const/volatile. Evita questo a meno che non ti blocchi con un'API const-errata.
(Molte spiegazioni teoriche e concettuali sono state fornite sopra)
Di seguito sono riportati alcuni esempi pratici quando ho usato static_cast , dynamic_cast , const_cast , reinterpret_cast .
(Fa riferimento anche a questo per comprendere la spiegazione: 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);
}
static_cast<char*>(&val)?
static_castfunziona solo tra tipi con conversioni definite, relazione visibile per eredità o da / a void *. Per tutto il resto, ci sono altri cast. reinterpret casta qualsiasi char *tipo è consentito consentire la lettura della rappresentazione di qualsiasi oggetto - e uno dei pochi casi in cui tale parola chiave è utile, non un generatore dilagante di implementazione / comportamento indefinito. Ma questa non è considerata una conversione "normale", quindi non è consentita dal (di solito) molto conservatore static_cast.
Potrebbe essere utile se conosci un po 'di interni ...
static_cast
static_castper loro.Aa B, il costruttore delle static_castchiamate Bpassa Acome parametro. In alternativa, Apotrebbe avere un operatore di conversione (ad es A::operator B().). Se Bnon ha tale costruttore o Anon ha un operatore di conversione, viene visualizzato un errore di tempo di compilazione.A*al B*riesce sempre se A e B sono in gerarchia di ereditarietà (o nulla) altrimenti si ottiene errore di compilazione.A&a B&.dynamic_cast
(Base*)per (Derived*)può riuscire se il puntatore non è effettivamente di tipo derivato.A*a B*, se cast non è valido, dynamic_cast restituirà nullptr.A&per B&se cast è valido quindi dynamic_cast lancerà un'eccezione bad_cast.const_cast
set<T>quali restituisce solo i suoi elementi come const per assicurarsi di non cambiare la sua chiave. Tuttavia, se il tuo intento è quello di modificare i membri non chiave dell'oggetto, allora dovrebbe essere ok. È possibile utilizzare const_cast per rimuovere la costanza.T& SomeClass::foo()e const T& SomeClass::foo() const. Per evitare la duplicazione del codice, è possibile applicare const_cast per restituire il valore di una funzione da un'altra.reinterpret_cast
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.Ottieni UB che può comportare un segfault in fase di esecuzione se sei fortunato. 2. I lanci dinamici possono essere utilizzati anche nel cross casting. 3. In alcuni casi i cast di costanti possono causare UB. L'uso mutablepuò essere una scelta migliore per implementare la costanza logica.
mutable, il casting incrociato ecc.
Ha questa risposta alla domanda?
Non ho mai usato reinterpret_cast, e mi chiedo se incappare in un caso che ne abbia bisogno non è un odore di cattivo design. Nella base di codice su cui lavoro dynamic_castè molto usata. La differenza static_castè che a dynamic_castesegue un controllo di runtime che può (più sicuro) o meno (più overhead) essere ciò che si desidera (vedere msdn ).
reinterpret_castper estrarre pezzi di dati da un array. Ad esempio, se ho char*un grande buffer pieno di dati binari compressi che devo spostare e ottenere singole primitive di vario tipo. Qualcosa del genere:template<class ValType> unsigned int readValFromAddress(char* addr, ValType& val) { /*On platforms other than x86(_64) this could do unaligned reads, which could be bad*/ val = (*(reinterpret_cast<ValType*>(addr))); return sizeof(ValType); }
reinterpret_cast, non ci sono molti usi per questo.
reinterpret_castusato solo per un motivo. Ho visto dati di oggetti grezzi archiviati in un tipo di dati "BLOB" in un database, quindi quando i dati vengono recuperati dal database, reinterpret_castvengono utilizzati per trasformare questi dati grezzi nell'oggetto.
Oltre alle altre risposte finora, qui è un esempio evidente dove static_castnon è sufficiente, quindi reinterpret_castè necessario. Supponiamo che ci sia una funzione che in un parametro di output restituisce puntatori a oggetti di classi diverse (che non condividono una classe base comune). Un vero esempio di tale funzione è CoCreateInstance()(vedi l'ultimo parametro, che è in realtà void**). Supponiamo che tu richieda una particolare classe di oggetti da questa funzione, quindi conosci in anticipo il tipo per il puntatore (cosa che fai spesso per gli oggetti COM). In questo caso non è possibile eseguire il cast del puntatore al puntatore void**con static_cast: è necessario reinterpret_cast<void**>(&yourPointer).
Nel codice:
#include <windows.h>
#include <netfw.h>
.....
INetFwPolicy2* pNetFwPolicy2 = nullptr;
HRESULT hr = CoCreateInstance(__uuidof(NetFwPolicy2), nullptr,
CLSCTX_INPROC_SERVER, __uuidof(INetFwPolicy2),
//static_cast<void**>(&pNetFwPolicy2) would give a compile error
reinterpret_cast<void**>(&pNetFwPolicy2) );
Tuttavia, static_castfunziona per semplici puntatori (non puntatori a puntatori), quindi il codice sopra può essere riscritto per evitare reinterpret_cast(a un prezzo di una variabile aggiuntiva) nel modo seguente:
#include <windows.h>
#include <netfw.h>
.....
INetFwPolicy2* pNetFwPolicy2 = nullptr;
void* tmp = nullptr;
HRESULT hr = CoCreateInstance(__uuidof(NetFwPolicy2), nullptr,
CLSCTX_INPROC_SERVER, __uuidof(INetFwPolicy2),
&tmp );
pNetFwPolicy2 = static_cast<INetFwPolicy2*>(tmp);
&static_cast<void*>(pNetFwPolicy2)invece di static_cast<void**>(&pNetFwPolicy2)?
Mentre altre risposte descrivono bene tutte le differenze tra i cast C ++, vorrei aggiungere una breve nota del perché non dovresti usare i cast in stile C (Type) vare Type(var).
Per i principianti C ++ i cast in stile C sembrano essere l'operazione superset sui cast C ++ (static_cast <> (), dynamic_cast <> (), const_cast <> (), reinterpret_cast <> ()) e qualcuno potrebbe preferirli ai cast C ++ . In effetti il cast in stile C è il superset e il più breve da scrivere.
Il problema principale dei cast in stile C è che nascondono la vera intenzione dello sviluppatore del cast. I cast in stile C possono eseguire praticamente tutti i tipi di cast da cast normalmente sicuri eseguiti da static_cast <> () e dynamic_cast <> () a cast potenzialmente pericolosi come const_cast <> (), dove il modificatore const può essere rimosso in modo che le variabili const può essere modificato e reinterpret_cast <> () che può anche reinterpretare i valori interi nei puntatori.
Ecco il campione.
int a=rand(); // Random number.
int* pa1=reinterpret_cast<int*>(a); // OK. Here developer clearly expressed he wanted to do this potentially dangerous operation.
int* pa2=static_cast<int*>(a); // Compiler error.
int* pa3=dynamic_cast<int*>(a); // Compiler error.
int* pa4=(int*) a; // OK. C-style cast can do such cast. The question is if it was intentional or developer just did some typo.
*pa4=5; // Program crashes.
Il motivo principale per cui i cast di C ++ sono stati aggiunti al linguaggio è stato quello di consentire a uno sviluppatore di chiarire le sue intenzioni - perché farà quel cast. Usando i cast in stile C che sono perfettamente validi in C ++, stai rendendo il tuo codice meno leggibile e più soggetto a errori soprattutto per gli altri sviluppatori che non hanno creato il tuo codice. Quindi, per rendere il tuo codice più leggibile ed esplicito, dovresti sempre preferire i cast C ++ rispetto ai cast in stile C.
Ecco una breve citazione dal libro di Bjarne Stroustrup (l'autore del C ++) The C ++ Programming Language 4th edition - page 302.
Questo cast in stile C è molto più pericoloso degli operatori di conversione nominati perché la notazione è più difficile da individuare in un programma di grandi dimensioni e il tipo di conversione previsto dal programmatore non è esplicito.
Per capire, consideriamo di seguito lo snippet di codice:
struct Foo{};
struct Bar{};
int main(int argc, char** argv)
{
Foo* f = new Foo;
Bar* b1 = f; // (1)
Bar* b2 = static_cast<Bar*>(f); // (2)
Bar* b3 = dynamic_cast<Bar*>(f); // (3)
Bar* b4 = reinterpret_cast<Bar*>(f); // (4)
Bar* b5 = const_cast<Bar*>(f); // (5)
return 0;
}
Solo la riga (4) viene compilata senza errori. Solo reinterpret_cast può essere utilizzato per convertire un puntatore a un oggetto in un puntatore a qualsiasi tipo di oggetto non correlato.
Uno da notare è: dynamic_cast non funzionerebbe in fase di esecuzione, tuttavia sulla maggior parte dei compilatori non riuscirà anche a compilare perché non ci sono funzioni virtuali nella struttura del puntatore da trasmettere, il che significa che dynamic_cast funzionerà solo con puntatori di classe polimorfici .
Quando utilizzare il cast C ++ :
static_castvs dynamic_castvs reinterpret_castinternals visualizza su un downcast / upcast
In questa risposta, voglio confrontare questi tre meccanismi su un esempio concreto di upcast / downcast e analizzare cosa succede ai puntatori / memoria / assembly sottostanti per dare una comprensione concreta di come si confrontano.
Credo che questo darà una buona intuizione su come questi cast siano diversi:
static_cast: esegue un offset dell'indirizzo in fase di runtime (basso impatto di runtime) e non verifica che un downcast sia corretto.
dyanamic_cast: esegue lo stesso offset dell'indirizzo durante il runtime static_cast, ma anche un costoso controllo di sicurezza che un downcast è corretto utilizzando RTTI.
Questo controllo di sicurezza consente di eseguire una query se un puntatore della classe base è di un determinato tipo in fase di esecuzione controllando un valore restituito nullptrche indica un downcast non valido.
Pertanto, se il tuo codice non è in grado di verificarlo nullptre di intraprendere un'azione valida di non interruzione, dovresti semplicemente utilizzare static_castinvece del cast dinamico.
Se un abort è l'unica azione che il tuo codice può intraprendere, forse vuoi solo abilitare dynamic_castin debug builds ( -NDEBUG), e usare static_castaltrimenti, ad esempio come fatto qui , per non rallentare le tue corse veloci.
reinterpret_cast: non fa nulla in fase di esecuzione, nemmeno l'offset dell'indirizzo. Il puntatore deve puntare esattamente al tipo corretto, nemmeno una classe base funziona. In genere non lo si desidera a meno che non siano coinvolti flussi di byte non elaborati.
Considera il seguente esempio di codice:
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;
}
Compila, esegui e disassembla con:
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
dove setarchviene utilizzato per disabilitare ASLR per facilitare il confronto delle esecuzioni.
Uscita possibile:
&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
Ora, come menzionato in: https://en.wikipedia.org/wiki/Virtual_method_table per supportare in modo efficiente le chiamate al metodo virtuale, la struttura dei dati di memoria Ddeve assomigliare a:
B1:
+0: pointer to virtual method table of B1
+4: value of int_in_b1
B2:
+0: pointer to virtual method table of B2
+4: value of int_in_b2
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
Il fatto chiave è che la struttura dei dati di memoria di Dcontiene al suo interno una struttura di memoria compatibile con quella di B1e quella B2interna.
Pertanto giungiamo alla conclusione critica:
un upcast o downcast deve solo spostare il valore del puntatore di un valore noto al momento della compilazione
In questo modo, quando Dviene passato all'array del tipo di base, il cast di tipo calcola effettivamente quell'offset e punta a qualcosa che sembra esattamente un valido B2in memoria:
b2s[1] = &d;
tranne che questo ha la vtable per Dinvece di B2, e quindi tutte le chiamate virtuali funzionano in modo trasparente.
Ora possiamo finalmente tornare al tipo di casting e all'analisi del nostro esempio concreto.
Dall'output stdout vediamo:
&d 0x7fffffffc930
b2s[1] 0x7fffffffc940
Pertanto, l'implicito static_castfatto lì ha calcolato correttamente l'offset dalla Dstruttura dati completa a 0x7fffffffc930 a quella B2simile che è a 0x7fffffffc940. Ne deduciamo anche che ciò che sta tra 0x7fffffffc930 e 0x7fffffffc940 è probabilmente i B1dati e la vtable.
Quindi, nelle sezioni downcast, ora è facile capire come falliscono quelli non validi e perché:
static_cast<D*>(b2s[0]) 0x7fffffffc910: il compilatore ha appena aumentato 0x10 in fase di compilazione byte per provare a passare da B2a al contenimentoD
Ma poiché b2s[0]non era un D, ora indica una regione di memoria indefinita.
Lo smontaggio è:
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)
quindi vediamo che GCC fa:
Dquale non esistedynamic_cast<D*>(b2s[0]) 0: C ++ in realtà ha scoperto che il cast non era valido e restituito nullptr!
Non è possibile farlo al momento della compilazione e lo confermeremo dallo smontaggio:
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)
Innanzitutto c'è un controllo NULL e restituisce NULL se l'ingresso è NULL.
Altrimenti, imposta alcuni argomenti in RDX, RSI e RDI e chiama __dynamic_cast.
Non ho la pazienza di analizzarlo ulteriormente ora, ma come altri hanno detto, l'unico modo per farlo funzionare è __dynamic_castquello di accedere ad alcune strutture di dati in memoria RTTI extra che rappresentano la gerarchia di classi.
Deve quindi iniziare dalla B2voce per quella tabella, quindi seguire questa gerarchia di classi fino a quando trova la vtable per un Dtypecast b2s[0].
Ecco perché reinterpretare il cast è potenzialmente costoso! Ecco un esempio in cui una patch di una riga che converte a dynamic_castin static_castin un progetto complesso ha ridotto il tempo di esecuzione del 33%! .
reinterpret_cast<D*>(b2s[1]) 0x7fffffffc940questo ci crede ciecamente: abbiamo detto che esiste un Dindirizzo b2s[1]e il compilatore non esegue calcoli di offset.
Ma questo è sbagliato, poiché D è in realtà a 0x7fffffffc930, ciò che è a 0x7fffffffc940 è la struttura simile a B2 all'interno di D! Quindi si accede alla spazzatura.
Possiamo confermarlo dall'assemblea orrenda -O0che sposta semplicemente il valore:
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)Domande correlate:
Testato su Ubuntu 18.04 amd64, GCC 7.4.0.