A livello astratto, puoi includere tutto ciò che desideri in una lingua che stai progettando.
A livello di implementazione, è inevitabile che alcune di queste cose siano più semplici da implementare, alcune saranno complicate, alcune possono essere rese veloci, altre sono destinate ad essere più lente, e così via. Per tenere conto di ciò, i progettisti spesso devono prendere decisioni e compromessi difficili.
A livello di implementazione, uno dei modi più veloci che abbiamo trovato per accedere a una variabile è scoprire il suo indirizzo e caricare il contenuto di tale indirizzo. Nella maggior parte delle CPU ci sono istruzioni specifiche per il caricamento dei dati dagli indirizzi e quelle istruzioni di solito devono sapere quanti byte devono caricare (uno, due, quattro, otto, ecc.) E dove inserire i dati che caricano (registro singolo, registro coppia, registro esteso, altra memoria, ecc.). Conoscendo la dimensione di una variabile, il compilatore può sapere esattamente quale istruzione emettere per gli usi di quella variabile. Non conoscendo la dimensione di una variabile, il compilatore dovrebbe ricorrere a qualcosa di più complicato e probabilmente più lento.
A livello astratto, il punto del sottotipo è di poter usare istanze di un tipo in cui è previsto un tipo uguale o più generale. In altre parole, può essere scritto un codice che prevede un oggetto di un determinato tipo o qualcosa di più derivato, senza sapere in anticipo cosa sarebbe esattamente questo. E chiaramente, poiché più tipi derivati possono aggiungere più membri di dati, un tipo derivato non ha necessariamente gli stessi requisiti di memoria dei suoi tipi base.
A livello di implementazione, non esiste un modo semplice per una variabile di dimensioni predeterminate di contenere un'istanza di dimensioni sconosciute e di avere accesso in un modo che normalmente chiamereste efficiente. Ma c'è un modo per spostare un po 'le cose e usare una variabile non per immagazzinare l'oggetto, ma per identificare l'oggetto e lasciare che l'oggetto sia immagazzinato altrove. In questo modo è un riferimento (ad es. Un indirizzo di memoria) - un ulteriore livello di riferimento indiretto che garantisce che una variabile debba contenere solo un tipo di informazioni di dimensioni fisse, purché sia possibile trovare l'oggetto attraverso tali informazioni. Per ottenere ciò, dobbiamo solo caricare l'indirizzo (dimensione fissa) e quindi possiamo lavorare come al solito usando quegli offset dell'oggetto che sappiamo essere validi, anche se quell'oggetto ha più dati negli offset che non conosciamo. Possiamo farlo perché non
A livello astratto, questo metodo consente di memorizzare un (riferimento a a) string
in una object
variabile senza perdere le informazioni che lo rendono a string
. Va bene per tutti i tipi di lavorare in questo modo e potresti anche dire che è elegante sotto molti aspetti.
Tuttavia, a livello di implementazione, il livello extra di indiretta comporta più istruzioni e sulla maggior parte delle architetture rende ogni accesso all'oggetto un po 'più lento. Puoi consentire al compilatore di ottenere più prestazioni da un programma se includi nella tua lingua alcuni tipi di uso comune che non hanno quel livello extra di riferimento indiretto (il riferimento). Ma rimuovendo quel livello di riferimento indiretto, il compilatore non può più permetterti di sottotipare in modo sicuro dalla memoria. Questo perché se aggiungi più membri di dati al tuo tipo e lo assegni a un tipo più generale, tutti i membri di dati extra che non rientrano nello spazio allocato per la variabile di destinazione verranno eliminati.