Programmazione orientata agli oggetti in ANSI-C. Un esempio – Set

main.c verrà compilato correttamente, ma prima di eseguire un programma, dobbiamo implementare i tipi di dati astratti e il gestore della memoria. Se un oggetto non memorizza alcuna informazione e se ogni oggetto appartiene a più di un set, possiamo rappresentare ogni oggetto e ogni set come un valore intero (piccolo, unico e positivo) usato come indice in un array heap[]. Se un oggetto è un membro di un set, il suo elemento array contiene il valore intero che rappresenta il set. Gli oggetti, allora, puntano al set che li contiene.

La prima soluzione è così semplice che combina tutta i moduli in un solo file Set.c. I Set e gli oggetti hanno la stessa rappresentazione, così new() non necessita di porre attenzione al descrittore del tipo. Restituirà semplicemente un elemento nell'heap[] con valore zero:

#if ! defined MANY || MANY < 1
#define MANY 10
#endif
static int heap [MANY];
void * new (const void * type, ...)
{ int * p; /* & heap[1..] */
for (p = heap + 1; p < heap + MANY; ++ p)
if (! * p)
break;
assert(p < heap + MANY);
* p = MANY;
return p;
}

Useremo un valore zero per marcare gli elementi disponibili nell'heap[]; di conseguenza, non possiamo restituire un riferimento a heap[0]- se ci fosse un set, i suoi elementi conterrebbero il valore indice zero.
Prima che un oggetto venga aggiunto al set, faremo in modo che contenga il valore indice impossibile MANY così che new() non possa trovarlo di nuovo. Ci servirà indicarlo così anche per non sbagliarci scambiandolo per un membro di un set.

new() può generare out of memory. Questo è solo il primo di molti errori che "non potrebbero accadere". Useremo pertnato la macro ANSI C assert() per marcare questi punti. Una implementazione più realistica dovrebbe come minimo stampare un messaggio di errore comprensibile oppure usare una funzione generale per l'error handling, funzione che potrebbe essere ridefinita dall'utente secondo necessità. Per i nostri scopi di descrizione di una tecnica di scrittura del codice, comunque, preferiamo limitare la minimo la difficoltà.
Nel capito 13, a tal scopo, presenteremo una tecnica di error handling per gestire le eccezioni.

Con la funzione delete() si deve prestare attenzione ai puntatori a null. Un elemento di heap[] viene riciclato impostandolo a zero:

void delete (void * _item)
{ int * item = _item;
if (item)
{ assert(item > heap && item < heap + MANY);
* item = 0;
}
}

Abbiamo la necessità di uniformare il modo con cui abbiamo a che fare con i puntatori generici; di conseguenza utilizzeremo come prefissi nei loro nomi un carattere di sottolineatura (underscore).

Un set è rappresentato dai suoi oggetti: ogni elemento punta al set. Se un elemento contenesse MANY, potrebbe essere aggiunto al set, altrimenti dovrebbe già essere presente nel set perchè non permettiamo che un oggetto appartenga a più di un set.

ansic_programmazione-codice

assert() richiede un po' di attenzione: vogliamo che i puntatori in heap[] e nel set non appartengano ad altri set, cioè il suo valore dell'array element dovrebbe essere MANY.

Le altre funzioni sono anch'esse semplici. find() cerca semplicemente se il suo elemento contiene l'indice appropriato per il set:

ansic_programmazione-codice

contains() converte il risultato di find() in un valore vero/falso

ansic_programmazione-codice

drop() può fare affidamento solo su find() per verificare se l'elemento da eliminare appartiene al momento al set. Se sì, marcheremo lo status con MANY:

ansic_programmazione-codice

Se fossimo più esigenti, potremmo fare sì che l'elemento di cui fare il drop non appartenga ad alcun altro set. In questo caso, comunque, non potremmo replicare la maggior parte del codice di find() in drop().

La nostra implementazione è abbastanza poco convenzionale. Non avremo bisogno di implementare differ() per gestire il set. Ma dobbiamo comunque implementarla perchè la nostra applicazione usa questa funzione.

ansic_programmazione-codice

Nella funzione differ() è sufficiente un semplice confronto fra puntatori.

Ecco qui tutta l'applicazione - per questa possibile soluzione non abbiamo utilizzato i descrittori Set e Object ma li dobbiamo definire per fare in modo che il compilatore C non abbia di che lamentarsi:

const void * Set;
const void * Object;
Useremo questi puntatori in main() per creare nuovi set e oggetti.

Scarica subito una copia gratis

Scrivi un commento

Seguici anche sul tuo Social Network preferito!

Send this to a friend