-- Introduzione
-- Obiettivi
Implementazione pratica
-- Dichiarazione di classe
Ereditarietà

Progetto Yed
Tecniche di programmazione ad oggetti con il linguaggio C.

Ereditarietà: un esempio pratico

Creiamo un nuovo oggetto, chiamato Cderiv, che eredita interfaccia ed implementazione dalla nostra Cprova dei paragrafi precedenti:

typedef struct
  {
  char PRIVATE(foo_priv);
  PUBLIC int (*metodo)(void *);
  PUBLIC char elemento;
  } Cprova;

Cderiv eredita da Cprova ed inoltre possiede i suoi metodi e i suoi attributi specifici. Dichiariamo il tutto nella forma esplicitata dalla regola n. 1 della nostra ereditarietà:

typedef struct{
// Ereditato da Cprova -----------
char PRIVATE(foo_priv); 
PUBLIC int (*metodo)(void *);    
PUBLIC char elemento; 
                               
// Entità proprie a Cderiv -----
int PRIVATE(siFoo);
PUBLIC int (*Foo_Func)(void *);
} Cderiv;

Il costruttore di Cderiv conterrà il costruttore di Cprova con l'inizializzazione delle entità peculiari all'oggetto:

Cderiv * _Cderiv(void) {
Cderiv *this=malloc(sizeof(Cderiv));
if(this!=NULL) {
  memset(this,0,sizeof(Cderiv));
  // INIT from Cprova constructor -----------
  this->metodo=metodo;
  this->PRIVATE(foo_priv)=24;
  this->elemento=12;
  // INIT for Cderiv entities -----------
  this->PRIVATE(siFoo)=32;
  this->Foo_Func=Foo_Func;
  }
return this;
}

Lo stesso avviene per il distruttore di Cderiv; nel nostro esempio, non sono necessarie particolari operazioni di rilascio della memoria utilizzata dall'oggetto, dunque la normale funzione 'free' è sufficiente; se invece si rendessero necessarie operazioni di rilascio di memoria da eseguirsi prima del rilascio globale, occorrerebbe mescolare il codice del distruttore di Cprova con quello di Cderiv, perchè Cprova è totalmente entrocontenuto in Cderiv.

void _DCderiv(void *pvT) {
free(pvT);
}

La funzione metodo, definita nell'oggetto Cprova, è stata precedentemente dichiarata con l'attributo PRIVATE_FUNCTION. Seguendo la regola n. 2 dell'ereditarietà nello scenario Yed, cancelliamo questo attributo dalla dichiarazione e dalla definizione della funzione.

/**** PROTOTIPO DEL METODO ****/
int metodo(void *);

/**** IMPLEMENTAZIONE DEL METODO ****/
int metodo(void *pVWork) {
Cprova *this=pvWork;        
printf("Invocazione metodo: elemento interno [%d]\n",
         this->PRIVATE(foo_priv));
return 0;
}

Come può la funzione metodo essere invocata correttamente sia da un'istanza di Cprova, sia da una istanza di Cderiv?
Molto semplicemente perchè l'interfaccia di Cprova esiste sia in Cprova sia in Cderiv allo stesso offset in memoria relativamente all'inizio della dichiarazione, come possiamo vedere da questa tabella di comparazione:

Dichiarazione Cprova

typedef struct
{
char PRIVATE(foo_priv);
PUBLIC int (*metodo)(void *);
PUBLIC char elemento;  

} Cprova;

Dichiarazione Cderiv

typedef struct
{
char PRIVATE(foo_priv);
PUBLIC int (*metodo)(void *);
PUBLIC char elemento;  
          
int PRIVATE(siFoo);
PUBLIC int (*Foo_Func)(void *);
} Cderiv;

Questa è la ragione per cui uno Yed derived object può ereditare da un solo Yed object, indifferentemente Yed base object oppure Yed derived object. Una esatta sequenza di dichiarazione delle entità garantisce infatti l'utilizzo corretto di tutti i metodi indipendentemente da dove sono stati dichiarati, evento non realizzabile se uno Yed derived object derivasse da più oggetti insieme. Per un caso come questo, occorrerebbe definire una nuova regola univoca di accesso allo stato dell'oggetto attraverso la dichiarazione appropriata delle entità coinvolte.

Per completare l'esempio, ecco un codice di prova che utilizza una istanza di Cderiv:

#include "yedstd.h"
#include "yedprova.h"
#include "cderiv.h"

main(){
Cderiv *cFoo;

cFoo=New(Cderiv);
if(cFoo!=NULL) {
  cFoo->metodo(cFoo);   // metodo definito in Cprova
  cFoo->Foo_Func(cFoo); // metodo definito in Cderiv
  }

Delete(Cderiv,cFoo);

}

Ereditare la sola interfaccia di uno Yed object è una operazione molto simile a questa: l'unica differenza sta nel fatto che l'intera implementazione dello Yed derived object ricade sulle sue spalle. Nel nostro esempio, la funzione metodo, dichiarata in Cprova, sarebbe definita in Cderiv, e dunque acquisirebbe conseguentemente il puntatore this direttamente al tipo Cderiv. Alla fine, l'esito è comunque equivalente.

Grazie a questo meccanismo, è possibile costruire oggetti Yed a partire da astrazioni pure, che possono successivamente essere specializzate da opportuni Yed derived object; il tutto, con una flessibilità ed una complessità minore, ma con una resa finale molto simile, ad equivalenti strutture complesse realizzate in Java, o C++.



Copyright (C) - Giulio A. - ynoxia(at)gmail(dot)com