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

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

Ereditarietà: funzionamento base

L'ereditarietà è uno degli assiomi fondamentali della programmazione orientata agli oggetti: nei linguaggi che la implementano, è possibile creare un oggetto B a partire dalle funzionalità già create in un altro oggetto A, o addirittura, in sintassi più evolute, create in più oggetti. Di fatto, l'oggetto B deriva dall'oggetto A perchè l'oggetto A gli dona tutte le sue caratteristiche peculiari; l'oggetto B può usarle così come sono o espanderle o modificarle sulla base del suo scopo.
Le possibilità di ereditare funzionalità da un oggetto possono essere distinte in due tipologie:

  • ereditare la sola interfaccia
  • ereditare interfaccia ed implementazione

Un oggetto Yed permette di realizzare in pratica l'assioma della ereditarietà. Tale assioma, in questo scenario, assume la forma descritta nelle regole seguenti.

  • Definiamo da ora in avanti Yed base object un puro oggetto Yed che non eredita nulla da nessun altro oggetto Yed.
  • Definiamo da ora in avanti Yed derived object un oggetto Yed che deriva da un altro oggetto Yed.
  • Uno Yed derived object può ereditare sia la sola interfaccia da un altro Yed object, sia interfaccia e implementazione.
  • Uno Yed derived object può ereditare da uno ed uno solo Yed object.
  • Uno Yed derived object può ereditare da uno Yed object che può essere sia Yed base object, sia Yed derived object.

In che modo viene realizzato il meccanismo della ereditarietà sulla base di queste regole?

Quando un oggetto A deriva da un oggetto B, A contiene tutte le entità di B più le proprie; questo primo passo si realizza facilmente dichiarando all'interno dell'interfaccia di uno Yed derived object tutte le entità dell'oggetto Yed da cui deriva, assieme alle sue entità specifiche.

Nel momento in cui si passa alle implementazioni, qui nascono i problemi seri. Il problema più grosso da risolvere è questo: dato che un metodo di un oggetto Yed acquisisce il suo stato attraverso un cast al puntatore this ricevuto in input, nella forma

....  
(tipo oggetto) *this=pvWork;
....  

come si può fare in modo che uno Yed derived object acquisisca correttamente il suo stato anche invocando un metodo definito nell'oggetto da cui deriva, che possiede un cast del puntatore this di tipo differente?
In altre parole, seguendo il nostro esempio, se costruiamo l'oggetto Cderiv e lo rendiamo uno Yed derived object di Cprova, la funzione metodo di Cprova possiede al suo interno un cast al puntatore this di questo tipo:

Cprova *this=pvWork;

Ne consegue che invocando la funzione metodo da una istanza di Cderiv, il puntatore this, invece di essere castato al tipo Cderiv, viene castato al tipo Cprova, con conseguenti risultati potenzialmente devastanti.

Per risolvere questo problema serio, si è sfruttata una via di compromesso; dato che il meccanismo di casting del puntatore this è imprescindibile, se si costruisce uno Yed derived object in modo che contenga le stesse entità dell'oggetto da cui deriva nel medesimo offset in memoria rispetto all'oggetto da cui deriva, l'operazione di cast, potenzialmente devastante, funziona correttamente perchè le entità coinvolte sono rimaste nel medesimo punto in entrambi gli oggetti.
Per permettere dunque a tutti i metodi di accedere in maniera corretta allo stato dell'oggetto senza invalidare il meccanismo di casting del puntatore 'this' all'interno dei metodi, è necessario che ogni entità sia dichiarata allo stesso offset in memoria, rispetto all'inizio della dichiarazione dell'interfaccia dell''oggetto, in tutti gli oggetti in cui appare, a partire ed incluso lo Yed base object da cui inizia a propagarsi attraverso il meccanismo dell'ereditarietà.

In altre parole, se i metodi e gli attributi di tipo PRIVATE e PUBLIC di un oggetto Yed che chiamiamo A sono dichiarati nello stesso offset in memoria all'interno di uno Yed derived object che chiamiamo B ( operazione che realizza l'ereditarietà di interfaccia ), allora è possibile l'invocazione di un metodo definito in A anche attraverso una istanza di B, perchè l'operazione di cast del puntatore 'this' al tipo 'A' all'interno del metodo stesso resta valida sia per una istanza di A, sia per una istanza di B.

Questo è il meccanismo su cui si fonda l'ereditarietà all'interno dello scenario Yed. Prima di introdurre esempi pratici che permetteranno di comprenderlo al meglio, occorre definire le regole base di dichiarazione di entità all'interno di oggetti Yed, seguendo le quali sarà possibile realizzare il meccanismo di ereditarietà tra oggetti.

  1. Tutte le entità all'interno di oggetti Yed vanno dichiarate nell'ordine seguente:
  • Intera interfaccia dell'oggetto Yed da cui deriva ( se presente )
  • Attributi PRIVATE
  • Metodi PUBLIC
  • Attributi PUBLIC
  1. I metodi che si propagano verso uno Yed derived object non possono avere l'attributo PRIVATE_FUNCTION nella loro definizione.

La regola 2 annulla l'isolamento dei metodi introdotto nel paragrafo 'Nascondere i metodi', ma è necessaria per propagare il più facilmente possibile l'implementazione dei metodi verso uno Yed derived object. Infatti, l'attributo PRIVATE_FUNCTION rende impossibile, per il linker, localizzare la funzione se essa è definita in un altro file, rendendo di fatto problematica se non impossibile la compilazione di oggetti le cui implementazioni sono contenute in file differenti.

Un esempio pratico di ereditarietà tra oggetti Yed è contenuto nel paragrafo Un esempio pratico.

 


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