In che modo gli "oggetti" OOP e le "classi" sono organizzati in memoria in termini di linguaggio assembly?

10

In che modo gli oggetti sono organizzati in memoria?

Ad esempio, so che una funzione è un pezzo di codice in memoria, che si aspetta i parametri attraverso lo stack e / oi registri e gestisce il proprio stack frame.

Ma gli oggetti sono una struttura molto più complicata. Come sono organizzati? Ogni oggetto ha "collegamenti" ai metodi e passa l'indirizzo a se stesso con quel metodo?

Sarebbe bello vedere una buona spiegazione di questo argomento.

UPD. Ho fatto la domanda più precisa, e sono principalmente interessato alle lingue di tipizzazione statica.

    
posta Nikolai Golub 16.07.2014 - 10:13
fonte

2 risposte

14

Se non ci sono invii dinamici (polimorfismo), i "metodi" sono solo funzioni zuccherine, forse con un parametro aggiuntivo implicito. Di conseguenza, le istanze di classi senza comportamento polimorfico sono essenzialmente C struct s allo scopo di generare codice.

Per la classica spedizione dinamica in un sistema di tipo statico, esiste fondamentalmente una strategia predominante: vtables. Ogni istanza ottiene un puntatore aggiuntivo uno che fa riferimento a (una rappresentazione limitata di) il suo tipo, soprattutto il vtable: un array di puntatori di funzione, uno per metodo. Poiché l'insieme completo di metodi per ogni tipo (nella catena di ereditarietà) è noto al momento della compilazione, è possibile assegnare indici consecutivi (0..N per metodi N) ai metodi e richiamare i metodi cercando il puntatore della funzione in il vtable usando questo indice (di nuovo passando il riferimento all'istanza come parametro aggiuntivo).

Per linguaggi basati su classi più dinamici, in genere le classi stesse sono oggetti di prima classe e ogni oggetto ha invece un riferimento al suo oggetto di classe. L'oggetto classe, a sua volta, possiede i metodi in qualche modo dipendente dalla lingua (in Ruby i metodi sono una parte fondamentale del modello a oggetti, in Python sono solo oggetti funzione con piccoli wrapper attorno ad essi). Le classi tipicamente memorizzano anche riferimenti alla loro superclasse e delegano la ricerca di metodi ereditati a quelle classi per facilitare la metaprogrammazione che aggiunge e altera i metodi.

Ci sono molti altri sistemi che non si basano sulle classi, ma differiscono in modo significativo, quindi selezionerò solo un'interessante alternativa di design: quando puoi aggiungere nuovi (set di) metodi a tutti i tipi a volontà ovunque il programma (ad esempio, classi di tipi in Haskell e tratti in Rust), l'insieme completo di metodi non è noto durante la compilazione. Per risolvere questo problema, si crea un per tratto e lo si passa in giro quando è richiesta l'implementazione del tratto. Cioè, codice come questo:

void needs_a_trait(SomeTrait &x) { x.method2(1); }
ConcreteType x = ...;
needs_a_trait(x);

è compilato in questo modo:

functionpointer SomeTrait_ConcreteType_vtable[] = { &method1, &method2, ... };
void needs_a_trait(void *x, functionpointer vtable[]) { vtable[1](x, 1); }
ConcreteType x = ...;
needs_a_trait(x, SomeTrait_ConcreteType_vtable);

Questo significa anche che le informazioni vtable non sono incorporate nell'oggetto. Se vuoi fare riferimento a una "istanza di un tratto" che si comporterà correttamente quando, ad esempio, sono memorizzati in strutture di dati che contengono molti tipi diversi, è possibile creare un indicatore di grasso (instance_pointer, trait_vtable) . Questa è in realtà una generalizzazione della strategia di cui sopra.

    
risposta data 16.07.2014 - 10:41
fonte
3

Questa è la risposta nel senso del proverbio "se dai a un uomo un pesce che gli dai da mangiare per un giorno, mentre se gli insegni a pescare dagli da mangiare per la vita" come la tua domanda è molto ampia

1. ricerca di fonti di informazione aperte

Google per "programmazione orientata agli oggetti dell'assieme" elenca molte risorse pertinenti diverse.

es. Programmazione orientata agli oggetti in Assembly, Ethan J. Eldridge, 15 dicembre 2011 è arrivato come terzo link e sembra buono

2. imparare dal codice scritto a mano esistente

Puoi vedere come funziona studiando i codici sorgente scritti con alcuni comuni linguaggi di assemblaggio con OOP esplicitamente dichiarato ad es.

3. imparare dai modelli di codice generati dai compilatori

Puoi vedere come funziona studiando i file di linguaggio degli assembly intermedi prodotti dai compilatori OOP di tua scelta. Sia i compilatori C ++ che quelli FreePascal possono essere configurati in modo che tu possa vedere come appare la trascrizione del codice OOP di alto livello nel codice del linguaggio assembly

4. risolvi il puzzle con il tuo buon senso

Ora è troppo tardi perché conosci già i suggerimenti di altre risposte. Ma prima che internet fosse così facile da cercare e prima che esistessero siti in cui è possibile ottenere la risposta senza sforzo da molte ore gratuitamente da alcuni volontari che l'hanno appresa nel modo più duro, l'unico metodo di lavoro disponibile gratuitamente per tutti era

Ask yourself: "how would I implement it?"

Molto spesso dopo diversi giorni trascorsi in giro per la mente e dopo aver buttato via diversi progetti di bozze di carta, si è scoperto che la soluzione che si presentava era molto simile alla soluzione che altri programmatori avevano ideato e già implementata

Ora la mia opinione è basata sulla risposta, non risponde direttamente alla domanda dell'OP e gli utenti senior di alto livello possono liberamente votare per me

    
risposta data 18.07.2014 - 22:55
fonte

Leggi altre domande sui tag