Programmazione orientata agli oggetti in ANSI-C. Manutenzione delle gerarchie di classe. Metaclassi

Il requisito n. 5 non è un vero e proprio problema, più che altro rappresenta un modo per risolvere gli altri. Proprio come un cerchio aggiunge informazioni ad un punto, così le descrizioni di classe per punti e cerchi assieme aggiungono informazioni – il draw() polimorfico – ad entrambe le descrizioni di classe delle due classi.

Detto in modo diverso: finchè due classi hanno gli stessi metodi linkati dinamicamente, anche se con implementazioni diverse, possono usare la stessa struct Class per memorizzare i link – è questo il caso di Point e Circle. Quando aggiungiamo un altro metodo linkato dinamicamente, dobbiamo estendere struct Class per fornire uno spazio per un nuovo link – ecco come otteniamo da una classe con un solo costruttore e un solo distruttore una classe come Point che a un componente .draw.

Estendere le strutture vuol dire utilizzare l'ereditarietà, ossia scopriamo che le descrizioni di classe con gli stessi insiemi di metodi formano una classe, e questo è proprio quello in cui consiste l'ereditarietà fra le classi di descrizioni di classe.

Chiameremo una classe di descrizioni di classe una metaclasse. Una metaclasse si comporta proprio come una classe: Point e Circle, le descrizioni di tutti i punti e di tutti i cerchi, sono due oggetti nella metaclasse PointClass, perchè entrambi descrivono come effettuare il disegno. Una metaclasse ha metodi: possiamo chiedere ad un oggetto Point o Circle le dimensioni degli oggetti – punti o cerchi – che descrive, oppure possiamo chiedere all'oggetto Circle se Point descrive la superclasse dei cerchi.

I metodi linkati dinamicamente possono fare cose diverse in relazione ad oggetti da classi diverse. Una metaclasse ha realmente bisogno di metodi dinamicamente linkati? Il distruttore in PointClass potrebbe essere chiamato in seguito ad una chiamata a delete(Point) oppure a delete(Circle), ossia quando cerchiamo di eliminare la descrizione di classe per i punti e i cerchi. Questo distruttore dovrebbe restituire un puntatore null perchè è chiaro che non è una buona idea eliminare una descrizione di classe. Un costruttore di metaclasse potrebbe essere più utile:

Circle = new(PointClass, /* ask the metaclass */
"Circle", /* to make a class description */
Point, /* with this superclass, */
sizeof(struct Circle), /* this size for the objects, */
ctor, Circle_ctor, /* this constructor, */
draw, Circle_draw, /* and this drawing method. */
0); /* end of list */

Questa chiamata produce una descrizione di classe per una classe i cui oggetti possono essere costruiti, distrutti e disegnati. Poichè il disegno è la nuova idea comune a tutte le descrizioni di classe in PointClass, sembra ragionevole aspettarsi che il costruttore PointClass sapesse come depositare un link in un metodo inerente al disegno nella nuova descrizione.

E' inoltre possibile vedere anche altri aspetti della situazione: se passiamo la descrizione della superclasse Point al costruttore di PointClass, questo dovrebbe essere in grado dapprima di copiare tutti i link ereditati da Point a Circle, e successivamente di sovrascrivere quelli che sono ridefiniti in Circle. Questo risolve completamente il problema dell'ereditarietà binaria: quando creiamo Circle specifichiamo solo i nuovi metodi specifici ai cerchi; metodi per punti sono implicitamente ereditari perchè i loro indirizzi possono essere copiati nel costruttore PointClass.

Scarica subito una copia gratis

Scrivi un commento

Seguici anche sul tuo Social Network preferito!

Send this to a friend