2011-12-18 18 views
5

secondo le severe norme di aliasing:char * conversione e aliasing regole

struct B { virtual ~B() {} }; 
struct D : public B { }; 

D d; 
char *c = reinterpret_cast<char*>(&d); 

Un char* a qualsiasi oggetto di tipo diverso è valido. Ma ora la domanda è, indicherà lo stesso indirizzo di & d? qual è la garanzia fatta da C++ Standard che restituirà lo stesso indirizzo?

+9

Penso che il tuo distruttore sia stato erroneamente chiamato –

+4

Non conosco la risposta. Ma, quando mai questa conoscenza ** sarà ** utile nella pratica? –

+0

Buona domanda. Alcuni cast possono effettivamente cambiare l'indirizzo (ad esempio quando sono coinvolte più eredità).Mi chiedo se sia così. – Kos

risposta

6

c e &d effettivamente hanno lo stesso valore, e se si reinterpretare pressofuso c di nuovo ad un D* si ottiene un puntatore valido che si può risolvere il riferimento. Inoltre, puoi trattare c come (puntatore al primo elemento di) una matrice opaca char[sizeof(D)] - questo è infatti lo scopo principale di puntare i puntatori ai puntatori di caratteri: Per consentire la serializzazione (de) (ad es. ofile.write(c, sizeof(D));), anche se generalmente solo fate questo per i tipi primitivi (e le loro matrici), poiché la disposizione binaria dei tipi composti non è generalmente specificata in modo portatile.

Come sottolinea giustamente @Oli e vorrei che io rinforzassi, non dovresti mai serializzare i tipi di composti nel loro complesso. Il risultato non sarà quasi mai deserializzabile, poiché l'implementazione di classi polimorfiche e padding tra campi di dati non è specificata e non è accessibile all'utente.

Si noti che reinterpret_cast<char*>(static_cast<B*>(&d)) può essere considerato come un array opaco char[sizeof(B)] con un ragionamento simile.

+0

Penso che forse dovresti chiarire che le classi di serializzazione/deserializzazione non POD come questa (cioè quelle con vptrs) sono una pessima idea. –

+0

@OliCharlesworth: Hm, gli * oggetti * non hanno alcun "vptrs" nascosto ... questo è tutto solo nell'implementazione di classe. Gli oggetti dovrebbero avere un layout abbastanza "normale", almeno fino a quando non si entra nell'eredità virtuale ... i soliti problemi di padding sono una ragione molto più immediata per non serializzare i tipi di classe in modo "ingenuo" (anche se poi di nuovo potrebbero esserci situazioni in cui è OK, ad es. cluster HPC di macchine identiche che utilizzano I/O di rete mappati in memoria ...). Attenzione al compratore :-) –

+0

Cosa? L'esistenza di funzioni virtuali significa che le istanze di classe devono avere un vptr (beh, in qualsiasi implementazione tipica). –

2

Sezione 5.2.10, punto 7 del C++ Standard 2003 dice:

Un puntatore a un oggetto può essere esplicitamente convertito in un puntatore ad un oggetto di tipo diverso. Tranne che la conversione di un valore di tipo "puntatore a T1" al tipo "puntatore a T2" (dove T1 e T2 sono tipi e dove i requisiti di allineamento di T2 non sono più rigidi di quelli di T1) e il suo tipo originale restituisce il valore del puntatore originale, il risultato di tale conversione del puntatore è non specificato.

Se per "stesso indirizzo" si intende "valore puntatore originale", quindi questa voce dice "sì".

0

L'intento è chiaro (e non qualcosa che deve essere discusso):

reinterpret_cast non cambia mai il valore di un indirizzo, a meno che il tipo di destinazione non può rappresentare tutti i valori di indirizzo (come un piccolo tipo intero, su un tipo di puntatore con allineamento intrinseco: ad esempio un puntatore che può rappresentare solo gli indirizzi pari, o i puntatori agli oggetti e i puntatori alle funzioni non possono essere mischiati ...).

La formulazione dello standard non riesce a catturarlo, ma ciò non significa che ci sia un vero problema pratico qui.

char *c = reinterpret_cast<char*>(&d); 

c punterà al primo byte di d, sempre.