Domanda

Come vengono archiviati gli oggetti in memoria in C ++?

Per una lezione normale come

class Object
    {
public:
    int i1;
    int i2;
    char i3;
    int i4;
private:
    };

L'uso di un puntatore di Object come un array può essere usato per accedere a i1 come segue?

((Object*)&myObject)[0] === i1?

Altre domande su SO sembrano suggerire che il cast di una struttura su un puntatore punterà al primo membro per i tipi POD. In che modo è diverso per le classi con i costruttori? Inoltre, in che modo differisce per i tipi non POD?

Modifica:

In memoria, quindi, la classe sopra sarebbe disposta come la seguente?

[i1 - 4bytes][i2 - 4bytes][i3 - 1byte][padding - 3bytes][i4 - 4bytes]
È stato utile?

Soluzione

Quasi. Trasmetti a un Oggetto * e trascuri di prendere un indirizzo. Ti chiediamo di nuovo come segue:

((int*)&myObject)[0] == i1

Devi stare molto attento con ipotesi come questa. Come hai definito la struttura, questo dovrebbe essere vero in qualsiasi compilatore che potresti incontrare. Ma ogni sorta di altre proprietà dell'oggetto (che potresti aver omesso dal tuo esempio), come altri hanno detto, lo renderanno non-POD e potrebbero (possibilmente in modo dipendente dal compilatore) rendere non vera l'istruzione sopra.

Nota che non sarei così veloce nel dirti che avrebbe funzionato se avessi chiesto informazioni su i3 - in quel caso, anche per un semplice POD, allineamento o endianness potrebbero facilmente rovinarti.

In ogni caso, dovresti evitare questo tipo di cose, se possibile. Anche se ora funziona bene, se tu (o qualcun altro che non capisce che stai facendo questo trucco) cambi mai l'ordine della struttura o aggiungi nuovi campi, questo trucco fallirà in tutti i luoghi in cui l'hai usato, che può essere difficile da trovare.

Rispondi alla tua modifica: se questa è la tua intera definizione di classe e stai utilizzando uno dei compilatori mainstream con opzioni predefinite e stai eseguendo un processore x86, allora sì, probabilmente hai indovinato il giusto layout di memoria. Ma la scelta del compilatore, delle opzioni del compilatore e della diversa architettura della CPU potrebbe facilmente invalidare i tuoi presupposti.

Altri suggerimenti

Le classi senza membri virtuali e senza eredità sono disposte in memoria proprio come le strutture. Ma quando inizi a ottenere livelli di ereditarietà le cose possono diventare complicate e può essere difficile capire quale ordine hanno le cose in memoria (in particolare l'eredità multipla).

Quando hai membri virtuali, hanno un " vtable " in memoria che contiene puntatori alla funzione effettiva che viene creata in base alla gerarchia ereditaria della classe.

La linea di fondo è: non accedere affatto alle classi in questo modo se puoi evitarlo (e inoltre non memorizzarle o memorizzarle). Se devi farlo (perché?), Assicurati di sapere esattamente come saranno gli oggetti della tua classe in memoria e fai attenzione a evitare l'ereditarietà.

La differenza è che questo trucco è valido solo per i tipi POD. Questo è davvero tutto ciò che c'è da fare. Lo standard specifica che questo cast è valido per un tipo POD, ma non fornisce alcuna garanzia su ciò che accade con i tipi non POD.

Dipende davvero dal compilatore, o meglio, è lasciato al compilatore per determinare il layout della memoria.

Ad esempio, è possibile disporre una combinazione di variabili membro pubbliche, private e protette in modo tale che ciascun tipo di accesso sia contiguo. Oppure, le classi derivate potrebbero avere variabili membro interlacciate con spazio inutilizzato nella super classe.

Le cose peggiorano con l'ereditarietà virtuale, dove le classi base praticamente ereditate possono essere disposte ovunque nella memoria allocata per quella particolare istanza.

POD è diverso perché deve essere compatibile con C.

Di solito ciò che conta non è se la classe ha un costruttore: ciò che conta è se la classe ha metodi virtuali. Per i dettagli, google per "vtable" e "vptr".

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top