Newsgroup: it.comp.lang.c++
Titolo: 3 Domande su ereditarieta' e polimorfismo

1 - Quando in una classe base imposto un metodo pure come protetto,
quando eredito in public, dove e' giusto definire questa funzione? Nella
parte protetta o privata della classe? Ereditando in public, la parte
corretta sarebbe provata, ma allora se dichiaro un metodo privato come
puro, come lo definisco dato che non posso accedere agli elementi
privati della classe base?
2 - E' sempre bene definire un distruttore virtuale in una classe base
se si fa uso di polimorfismo, anche se vuoto. In questo caso e' meglio
definire nell'header un distruttore virtuale vuoto oppure impostarlo
come puro? (So che puo' cambiare a seconda del design, ma ci sono
preferenze?)
3 - E' inutile definire come virtual metodi privati e protetti delle
classi, dato che in una gerarchia di funzioni i metodi privati e
protetti non possono essere chiamati dal codice esterno? Ogni funzione
virtuale che viene richiamata fa sempre riferimento alle funzioni
residenti nella propria classe giusto?

residenti nella propria classe giusto?
Un metodo privato dovrebbe essere usato come dici tu. Un metodo privato
e' ad uso esclusivo di una implementazione di un altro metodo della
stessa
classe. E' per questo che, nel senso paradigmatico, se una classe
rappresenta 
una interfaccia i suoi metodi non sono mai privati!
Non vero per il caso protected. Un metodo protetto puo' essere chiamato
sia
da quella classe che dalle sue derivate. Specializzare un metodo
protetto ha
senso eccome. Ti faccio un esempio:
// Definisce una astrazione basata su due algoritmi collegati
// che chiamiamo Algo1 e Algo2. 
// Ci sono molte interfaccie di questo genere. Per esempio LIFO e
// FIFO sono astrazioni (diverse) basate su due algoritmi (push e pop).
class Interfaccia {
public:
	// documentare la semantica dell'astrazione
	virtual void Algo1()=0;
	virtual void Algo2()=0;
protected:
	// possiamo omettere questa riga, 
	// ma rinforziamo il fatto che la classe non e' 
	// pubblicamente instanziabile
	Interfaccia(){}  
public:
	// SEMPRE virtuale. 
	// Puo' essere puro o meno, dato che ci sono comunque
	// altri metodi puri, se non ci fossero dovrebbe essere puro
	// per rendere astratta la classe.
	// Protetto o pubblico dipende dalla filosofia di gestione 
	// delle istanze. Se c'e' un garbage collector il problema
	// non si pone!
	virtual ~Interfaccia() { }
};
// Definisce una implementazione parziale specifica dell'astrazione.
// Per esempio una implementazione parziale specifica dell'astrazione
// "sorter" e' "quick sorter" e un'altra e' "merge sorter".
// L'implementazioe e' parziale perche' l'algoritmo e' definito in 
// funzione di sotto algoritmi (operazioni piu' elementari). 
// Classico caso di decomposizione top-down. Ovvero di specializzazione.
class InterfacciaModo1: public Interfaccia {
public:
	// documentare la specifica implementazione parziale
	void Algo1() { /* implementa Algo1 chiamando Op1 e Op2 */ }
	void Algo2() { /* implementa Algo2 chiamando Op1 e Op3 */ }
protected:
	// possiamo omettere questa riga, 
	// ma rinforziamo il fatto che la classe non e' 
	// pubblicamente instanziabile
	InterfacciaModo1(){}  
	// le tre operazioni elementari usate nelle implementazioni
	// parziali di Algo1 e Algo2
	// Ovviamente documentare la loro semantica
	virtual void Op1()=0;
	virtual void Op2()=0;	
	virtual void Op3()=0;	
public:
	// Idem come sopra. L'importante e' che e' virtuale
	// e che almeno un metodo resti astratto.
	~InterfacciaModo1() { }
};
// Definisce una implementazione completa specifica ed instanziabile.
// Per esempio "merge sort di interi".
class OggettoCheSupportaIlModo1diInterfaccia: public InterfacciaModo1 {
public:
	// costruttore/i pubblico. L'oggetto e' instanziabile
	OggettoCheSupportaIlModo1diInterfaccia() { }
	// distruttore pubblico e non astratto. L'oggetto e'
instanziabile
	~InterfacciaModo1() { }
	// aggiungere evantuali altri costruttori, copiatori, etc.
protected:
	// implementazione specifica delle operazioni elementari
	void Op1() { }
	void Op2() { }	
	void Op3() { }
private:
	// data members ed eventuali operazioni a basso livello usate
	// per semplificare o rendere piu' leggibile il codice di 
	// Op1, Op2 e Op3.
};
Ti faccio notare che quando nel body di InterfacciaModo1::Algo1() 
viene chiamata Op1, dato che il metodo e' virtuale verra' chiamata
l'implementazione effettiva che nel nostro caso sara' 
OggettoCheSupportaIlModo1diInterfaccia::Op1().
Ovviamente da qui' in poi si puo' estendere il nostro lattice di classi
con 
class AltroOggettoCheSupportaIlModo1diInterfaccia: public
InterfacciaModo1 ...
oppure con 
class InterfacciaModo2: public Interfaccia ...
class InterfacciaModo3: public Interfaccia ...
o anche con 
class VariazioneModo1: public InterfacciaModo1 ... 
che ridefinisce Algo2 e da una specializzazione parziale di Op1 basata
su Op3 e Op4 (qui introdotta).
e quindi con
class SupportaVariazione: public VariazioneModo1 ...
che e' instanziabile e implementa Op2, Op3 e Op4.
E cosi' via.
-- 
Posted from 
adsl-21-135.38-151.net24.it
 [151.38.135.21] 
via Mailgate.ORG Server - 
http://www.Mailgate.ORG