Programmazione orientata agli oggetti in ANSI-C. Metodi, Messaggi, Classi ed Oggetti

delete() deve essere in grado di localizzare il distruttore senza sapere che tipo di oggetto le è stato passato. Di conseguenza, rivisitando le dichiarazioni fatte nella sezione 2.1 dobbiamo fare in modo che il puntatore usato per localizzare il distruttore si trovi all'inizio rispetto a tutti gli oggetti passati a delete(), indipendentemente dal loro tipo.

A che cosa dovrebbe puntare il puntatore? Se avessimo l'indirizzo di un oggetto, questo puntatore ci permetterebbe di accedere a informazioni specifiche a seconda del tipo di oggetto, come ad esempio la sua funzione distruttore. Sembra proprio che stiamo per inventare una serie di funzioni specifiche per tipo, come ad esempio la funzione per mostrare gli oggetti, oppure la funzione per confrontare gli oggetti differ(), oppure la funzione clone() per creare una copia completa di un oggetto. Useremo un puntatore ad una tabella di funzioni con puntatori.

Ragionando più attentamente, potremmo renderci conto come questa tabella debba essere parte della descrizione di tipo passata a new() e l'ovvia soluzione al problema è lasciare che un oggetto punti all'intera descrizione di tipo:

struct Class {
size_t size;
void * (* ctor) (void * self, va_list * app);
void * (* dtor) (void * self);
void * (* clone) (const void * self);
int (* differ) (const void * self, const void * b);
};
struct String {
const void * class; /* must be first */
char * text;
};

struct Set {
const void * class; /* must be first */
...
};

Ogni oggetto inizia con un puntatore alla sua specifica descrizione di tipo e attraverso questa descrizione di tipo possiamo localizzare le informazioni specifiche sul tipo dell'oggetto: .size è la lunghezza che new() alloca per l'oggetto; ctor punta al costruttore chiamato da new() che riceve l'area allocata e il resto della lista di argomenti passata da new() in origine; .dtor punta al distruttore chiamato da delete() che riceve l'oggetto da distruggere; .clone punta a una funzione di copia che riceve l'oggetto che deve essere copiato, e .differ punta a una funzione che confronta l'oggetto con qualcos'altro.

Scorrendo la lista di cui sopra, noteremo che ogni funzione lavora sull'oggetto per il quale viene attivata. Solo il costruttore avrà a che fare con un'area di memoria parzialmente inizializzata. Chiameremo queste funzioni metodi per gli oggetti. Chiamare un metodo determina utilizzare un messaggio e abbiamo marcato l'oggetto ricevente il messaggio con il nome di parametro self- Siccome stiamo usando funzioni C semplici, self non ha la necessità di essere il primo parametro.

Molti oggetti condividono lo stesso descrittore di tipo, in altre parole necessitano della stessa quantità di memoria e degli stessi metodi. Chiameremo tutti gli oggetti con lo stesso descrittore di tipo classe; un singolo oggetto verrà chiamato istanza della classe. In questo modo una classe, un tipo di dato astratto, e un set di possibili valori assieme alle operazioni (ossia i tipi di dato) sono pressochè la stessa cosa.

Un oggetto è un'istanza della classe: possiede uno stato rappresentato dalla memoria allocata da new() e lo stato viene modificato e manipolato dai metodi della classe. Da un punto di vista puramente convenzionale, un oggetto è un valore di un particolare tipo di dato.

Scarica subito una copia gratis

Scrivi un commento

Seguici anche sul tuo Social Network preferito!

Send this to a friend