PDA

Visualizza la versione completa : c++ ereditarietà o template?


lio.b
14-09-2010, 17:06
Salve a tutti,
qualcuno può spiegarmi che differenza c'è tra ereditarietà e template? Ho provato a leggerlo sul libro di bruce eckel, il quale spiega il perchè si sono scelti i template e cosa cambia rispetto a java, ma francamente è più comprensibile un libro di filosofia scritto in linguaggio finnico. Qualcuno può aiutarmi?

MItaly
14-09-2010, 21:17
Si tratta di due tecniche differenti e non mutualmente esclusive, che anzi possono essere usate in abbinata con ottimi risultati.
L'ereditarietà consente di creare gerarchie di classi, in modo che le classi derivate ereditino dalle classi da cui derivano metodi e campi, e assieme al polimorfismo consente di fare sì che tante classi specializzate (le classi derivate) abbiano un'interfaccia comune (la classe base).
I template invece consentono di scrivere funzioni e classi in grado di operare su tipi generici, che sottostiano semplicemente ad una serie di requisiti (ad esempio siano copiabili o implementino un operatore di confronto).
Java "predilige" un approccio OOP "puro", ossia implementa la sua libreria tramite l'ereditarietà e il polimorfismo; il C++, invece, è un linguaggio a paradigma misto, per cui addirittura una parte della libreria standard (ad esempio gli iostreams) è implementato secondo l'OOP "classico", mentre tutta la parte di contenitori dati (la STL) è implementata secondo un approccio di "generic programming", basato completamente sui template.
Se un approccio OOP "classico" vorrebbe che i contenitori fornissero vari metodi per operare su di essi (che so, Sort, Reverse, ...), la STL fornisce dei contenitori e degli algoritmi generici, in grado cioè di operare su qualunque oggetto che abbia le caratteristiche di un iteratore.
Un iteratore è in un certo senso l'astrazione del puntatore, ossia un oggetto a cui può essere chiesto a cosa punta (operatore *), e, a seconda dei casi, di passare ad esempio all'elemento successivo o precedente (operatori ++ e --) o ad un elemento qualunque un tot più avanti o indietro (operatori + e -), o ancora, di poter essere confrontato con un altro iteratore (operatore != o addirittura <).
Ogni algoritmo ha una serie di requisiti; ad esempio un algoritmo di somma di tutti gli elementi nel range specificato necessiterà semplicemente che l'iteratore che gli viene passato sia incrementabile di uno e confrontabile con un altro iteratore (quello usato per segnalare la fine del range):


template<typename ForwardIter>
iterator_traits<InputIterator>::value_type SumAll(ForwardIter First, ForwardIter Last)
{
iterator_traits<InputIterator>::value_type result;
for(;First!=Last;++First)
result+=*First;
return result;
}

Codice scritto in questa maniera può essere un po' ostico da leggere agli inizi, ma consente una flessibilità notevolissima: un algoritmo del genere infatti si può utilizzare su qualunque contenitore che fornisca iteratori di questo genere: sia esso un vector, una lista o una mappa, non importa, il metodo di organizzazione interna dei dati è astratto dagli iteratori. La cosa interessante è che i normali puntatori possono essere considerati degli iteratori, per cui la funzione in questione può essere usata anche con i normali vettori C.

Ora vado a mangiare, more to come questa sera. :)

lio.b
14-09-2010, 23:20
Ok... ma ho ancora dei dubbi. Nel momento in cui ho un iteraotre come dici te posso semplicemente implementare quel metodo (che hai postato) in una classe iterator e dopodichè usarlo nelle classe derivate col polimorfismo. Questo è il tipico uso di linguaggi come java e non vedo cosa possa darmi in più l'uso dei template. Ma a livello di performance sono uguali?

MItaly
14-09-2010, 23:54
Originariamente inviato da lio.b
Ok... ma ho ancora dei dubbi. Nel momento in cui ho un iteraotre come dici te posso semplicemente implementare quel metodo (che hai postato) in una classe iterator e dopodichè usarlo nelle classe derivate col polimorfismo. Questo è il tipico uso di linguaggi come java e non vedo cosa possa darmi in più l'uso dei template.
Ma a livello di performance sono uguali?

Non hai capito. L'algoritmo non è membro di una classe iterator, l'algoritmo è una funzione a sé stante che si può usare passandole qualunque tipo di iteratore.
Il punto è che un modello classico OOP presupporrebbe che tutti gli iteratori ereditassero da una classe base comune, e tutti gli algoritmi andrebbero implementati o a livello di container, o a livello di un tipo particolare di iteratore. Probabilmente sarebbe necessario ricorrere a casini strani di ereditarietà multipla, la questione sarebbe molto scomoda da gestire, sarebbe necessario usare sempre chiamate virtuali, che hanno un certo overhead e non si possono espandere inline.

Al contrario, in questa maniera il design è molto più pulito: l'algoritmo è una funzione a sé stante che suppone di avere iteratori (che possono essere di qualunque tipo, non è necessario che ereditino da una superclasse comune) che dispongono di determinati operatori, ciascun container fornisce i suoi iteratori, che possono essere implementati in qualunque maniera purché forniscano gli operatori che servono ai vari algoritmi. Si ottiene così il massimo della flessibilità, senza contare che si possono evitare le chiamate virtuali ed espandere in linea i vari operatori, la qual cosa porta a vantaggi prestazionali notevoli, dato che gli iteratori di rado contengono procedure più lunghe di poche linee di codice.

lio.b
15-09-2010, 11:42
Innanzitutto grazie per la pazienza e le informazioni ma io continuo a non vedere un senso in tutto ciò...neanche sull'ipotesi dell'eridarietà multipla. Ragiono in java:
Mi serve un iteratore pronto? Uso il package di riferimento.
Se me ne serve uno mio creo una classe base e poi la estendo aggiungendo altre caratteristiche.
Qualora mi serva una classe che prende caratteristiche da più di una ne creo semplicemente una nuova. Alla fine i template fanno la stessa cosa perchè se mi serve un container che abbia caratteristiche che derivano da altri due devo cmq crearne uno nuovo.

shodan
15-09-2010, 13:55
Più o meno anche in C++. La differenza è che usando i template non leghi il codice a uno specifico tipo di dato, ma a uno generico che verrà esplicitato in seguito.
Prendi la classica funzione max


template <typename Tx>
const Tx& max(const Tx& a, const Tx&b) {
return ( a < b ) ? b : a;
}


Quante volte dovresti riscrivere quel codice per adattarlo, ad esempio ai tipi base? E per una classe?
Con un template lo scrivi una volta. Sarà il compilatore a scrivere il resto per te.
Altro esempio (molto semplificato preso dallo standard):


template <typename Tx, std::size_t N>
class array {
Tx data[N];
public:
Tx& operator[](std::size_t n) {
return data[N];
}
};

Quante volte dovresti riscrivere in Java quel codice per adattarlo, ad esempio ai tipi base? E per una classe?
Del resto non credo sia un caso se in Java siano stati introdotti i generics.

MItaly
15-09-2010, 14:41
In realtà il problema in Java è un po' mitigato dal fatto che tutto (tranne forse la versione "normale" dei tipi primitivi, se le mie vaghe reminiscenze di Java sono corrette) eredita da Object, per cui per si possono scrivere funzioni generiche usando Object come tipo. Tuttavia, i problemi vengono rilevati solo a runtime, e comunque non è possibile effettuare l'inlining degli operatori.

lio.b
15-09-2010, 17:23
Ok ok grazie infinite a tutti....l'esempio di shodan mi ha chiarito molte cose...posso chiederti cos'è quell'uso della &?
Io dal C ricordo che va messo davanti alla variabile, per esempio &a rappresenta l'indirizzo di a. Messo davanti come nel tuo esempio "Tx& operator" che significa?

MItaly
15-09-2010, 17:42
http://www.parashift.com/c++-faq-lite/references.html

lio.b
23-09-2010, 10:46
Scusate,
mi son fatto un giro in internet in questi giorni ma non capisto questa la differenze tr questi due metodi:
void function1 (int& a) ed void function(int &a).
Cosa cambia nel mettere la "&" vicino il tipo di dato anzichè vicino il nome della variabile?

Loading