Programmazione orientata agli oggetti in ANSI-C. Selettori, Dynamic Linkage e Polimorfismo 1/2

Chi realizza in pratica il cosiddetto messaging? Il costruttore chiamato da new() per allocare una nuova area di memoria non è praticamente inizializzato:

void * new (const void * _class, ...)
{ const struct Class * class = _class;
void * p = calloc(1, class —> size);
assert(p);
* (const struct Class **) p = class;
if (class —> ctor)
{ va_list ap;
va_start(ap, _class);
p = class —> ctor(p, & ap);
va_end(ap);
}
return p;
}

L'esistenza di un puntatore a struct Class all'inizio di un oggetto è estremamente importante. Ciò perchè inizializziamo questo puntatore in new():
La descrizione di tipo class a destra nella figura precedente viene inizializzato al tempo di compilazione. L'oggetto viene creato a tempo di esecuzione e i puntatori (rappresentati dalle linee tratteggiate) sono quindi inseriti. Nell'istruzione di assegnamento:

* (const struct Class **) p = class;

p punta all'inizio della nuova area di memoria dell'oggetto. Forziamo la conversione di p che tratta il principio dell'oggetto come un puntatore a struct Class e impostiamo l'argomento class come il valore del suo puntatore.

In seguito, se il costruttore è parte della descrizione di tipo, lo chiameremo ed esso restituirà il risultato di new(), in altre parole restituirà il nuovo oggetto. La Sezione 2.6 illustrerà che un costruttore intelligente può influire positivamente sulla gestione della memoria.

Da notare che solo le funzioni esplicitamente visibili come new() possono avere una lista di parametri variabile. A questa lista si accede con la variabile va_list ap che vine inizializzata utilizzando la macro va_start() che si trova in stdarg.h. new() può solamente pssare l'intera lista al costruttore, pertanto .ctor viene dichiarato con un parametro va_list e non con la sua propria lista di parametri variabile. Poichè in seguito potremmo voler condividere i parametri originali fra più funzioni, passiamo l'indirizzo di ap al costruttore - quanto il costruttore terminerà, ap punterà al primo argomento non utilizzato dal costruttore.

delete() ipotizza che ogni oggetto, ossia ogni puntatore non nullo, punta alla descrizione di tipo. Ciò viene usato per chiamare il distruttore se esiste, Qui, self gioca il ruolo di p nella figura precedente. Forziamo la conversione usando una variabile locale cp così come in questa porzione di codice:

void delete (void * self)
{ const struct Class ** cp = self;
if (self && * cp && (* cp) —> dtor)
self = (* cp) —> dtor(self);
free(self);
}

Il distruttore, anch'esso, può sostituire il suo proprio puntatore per essere passato a free() dalla funzione delete(). Se il costruttore non è ottimanale, il distruttore ha la possibilità di correggere la situazione, vedere la sezione 2.6 per delucidazioni. Se un oggetto non vuole essere cancellato, il suo distruttore potrà ritornare il puntatore null.

Tutti gli altri metodi memorizzati nella descrizione di tipo sono chiamati in modo analogo. In ogni caso, avremo un singolo oggetto ricevente self e dovremmo indirizzare la chiamata al metodo attraverso il suo descrittore:

int differ (const void * self, const void * b)
{ const struct Class * const * cp = self;
assert(self && * cp && (* cp) —> differ);
return (* cp) —> differ(self, b);
}
Scarica subito una copia gratis

Scrivi un commento

Seguici anche sul tuo Social Network preferito!

Send this to a friend