Ereditarietà, Riusabilità del codice e approfondimenti. Riepilogo

Gli oggetti di una superclasse e di una sottoclasse sono simili ma non identici nel comportamento. Gli oggetti della sottoclasse hanno generalmente uno stato la cui descrizione può essere più complessa e tipicamente dispongono di più metodi – in altre parole gli oggetti della sottoclasse sono versioni specializzate degli oggetti della superclasse.

Iniziamo la rappresentazione di un oggetto della sottoclasse con una copia della rappresentazione dell'oggetto della superclasse, ossia un oggetto della sottoclasse è rappresentato aggiungendo componenti alla fine di un oggetto della superclasse.

Una sottoclasse eredita i metodi della sua superclasse: poichè l'incipit di un oggetto della sottoclasse assomiglia all'incipit di un oggetto della superclasse, possiamo effettuare l'up-cast e vedere un puntatore all'oggetto della sottoclasse come un puntatore ad un oggetto della superclasse che possiamo passare ad un metodo della superclasse. Per evitare esplicite conversioni, si possono dichiarare i parametri dei metodi con void* come puntatori generici.

L'ereditarietà può essere vista come una forma rudimentale di polimorfiscmo: un metodo della superclasse accetta oggetti di tipi diversi, più precisamente oggetti della propria classe e quelli di tutte le sottoclassi. Ad ogni modo, poichè gli oggetti sono utilizzati come oggetti della superclasse, il metodo funziona solo nella parte relativa alla superclasse di ogni oggetto.

I metodi linkati dinamicamente possono essere ereditati da una superclasse o sovrascritti in una sottoclasse. Se un metodo linkato dinamicamente viene richiamto per un oggetto, raggiungeremo sempre il metodo che appartiene alla vera classe dell'oggetto, anche se del puntatore viene fatto l'up-cast in una qualsiasi superclasse. Se un metodo linkato dinamicamente viene ereditato, può funzionare solo sulla parte relativa alla superclasse dell'oggetto, perchè non conosce l'esistenza di una sottoclasse. Se il metodo viene invece sovrascritto, la versione del metodo nella sottoclasse può accedere all'intero oggetto e può addirittura chiamare il suo metodo corrispondente nella superclasse attraverso l'uso esplicito della descrizione di tipo della superclasse.

In particolare, i costruttori dovrebbero chiamare i costruttori della superclasse via via fino a raggiungere l'estremo antenato, in modo che ogni costruttore della sottoclasse lavori esclusivamente sulla estensione insita nella sottoclasse stessa. Ogni distruttore della sottoclasse dovrebbe rimuovere le risorse delle sottoclassi e quindi chiamare il distruttore della superclasse e così via fino all'ultimo degli antenati. La costruzione procede dall'antenato alla ultima sottoclasse, la distruzione funzione invece nell'ordine inverso.

La nostra strategia presenta un neo: in generale non potremmo chiamare un metodo linkato dinamicamente all'interno di un costruttore perchè l'oggetto potrebbe non essere inizializzato completamente. New() inserisce la descrizione di tipo all'interno di un oggetto prima che il costruttore venga chiamato. Di conseguenza se un costruttore chiama un metodo linkato dinamicamente su di un oggetto, non necessariamente raggiunge il metodo nella stessa classe del costruttore. Per evitare problemi, una tecnica più corretta sarebbe quella di fare in modo che il costruttore chiami il metodo attraverso il suo nome interno nella stessa classe, ad esempio per i punti chiamare Points_draw() puttosto che draw().

Per incoraggiare il rispetto del principio di information hiding, implementiamo una classe con 3 files. Il file interfaccia che contiene la descrizione di tipo di dato astratto, il file di rappresentazione che contiene la struttura di un oggetto e il file di implementazione che contiene il codice dei metodi ed inizializza la descrizione di tipo.
Un file di interfaccia include il file interfaccia della supercalsse e viene incluso per l'implementazione così come viene incluso in ogni applicazione che ne facci auso. Un file di rappresentazione include il file di rappresentazione della superclasse e viene incluso solo per l'implementazione della classe.

I componenti di una superclasse non dovrebbero essere referenziati direttamente in una sottoclasse. D'altro canto possiamo comunque provvedere a fornire metodi di accesso e di modifica per ogni componente, oppure possiamo aggiugere opportune macro nel file di rappresentazione della superclasse. La notazione funzionale rende molto semplice usare un text editor o un debugger per verificare eventuali punti di information leakage (perdita di informazioni o possibilità di corruzione delle stesse).

Scarica subito una copia gratis

Scrivi un commento

Seguici anche sul tuo Social Network preferito!

Send this to a friend