Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
Corso di
Linguaggi di Programmazione ad Oggetti 1
a cura di:
Giancarlo Cherchi
Introduzione
Cosa sono i Template?
TEMPLATE ? MODELLI
l Funzioni generiche
l Classi generiche
Esempio:
void main () {
int a = 7;
char b = ‘F’;
void main() {
int i = 5, j = 10;
char a = ‘A’, b = ‘B’;
swap (i, j); // Versione modificata!
swap (a, b); // Istanza di funzione generica
}
Limitazioni e restrizioni
l Una funzione generica deve eseguire le stesse
operazioni generali su tutte le sue versioni (a
differenza di una funzione modificata tramite
overloading)
l Una funzione virtuale non può essere template
l I distruttori non possono essere template
l Non possono essere utilizzate specifiche di link
diverse da C++ (extern “lang” prot)
Uso delle funzioni generiche
l Possono essere applicate a varie situazioni
l In particolare, una funzione che definisce un
algoritmo generalizzabile può essere trasformata
in una funzione template
l In questo modo, potrà essere utilizzata con
qualsiasi tipo di dati senza necessità di ricodifica
Esempio: BubbleSort generico
template <typename X>
void bubble (X * items, int dim) {
for (int a = 1; a < dim; ++a)
for (int b = dim-1; b >= a; --b)
if (items[b-1] > items[b])
swap(items[b], items[b-1]);
}
Esempio: BubbleSort generico
void main() {
int iarray[7] = {7,5,4,3,9,8,6};
double darray[5] = {4.3,2.5,-0.9,10.1,3.2};
nome-classe<tipo> oggetto;
Classi generiche
l Per definire i metodi, bisogna dichiarare per ogni
membro che si tratta di codice relativo ad un
template e specificare affianco ai simboli di
classe il parametro del template
l Un template può avere un numero qualunque di
parametri, che possono anche essere di un tipo
standard (int, float…)
Esempio senza template
class stack_int { class stack_float {
int dim, pos; int dim, pos;
int *vet; float *vet;
public: public:
stack_int (int max) { stack_int (int max) {
dim=max; pos=-1; dim=max; pos=-1;
vet=new int[max]; } vet=new float[max]; }
void push (int elm); void push (float elm);
int top(); float top();
}; };
Esempio con template
template <typename T> void main() {
class stack { stack<int> sti;
int dim, pos; stack<float> stf;
T *vet; stack<char *> stc;
public:
stack (int max) {
sti.push(25);
dim=max; pos=-1;
vet=new T[max]; } stf.push(3.14f);
void push (T elm); int n = stf.top();
T top(); stc.push (“stringa!”);
}; }
Esempio con più parametri
template <typename Key, typename Value, int size>
class AssocArray {
static const int kSize;
Key keyArray [size];
Value valueArray [size];
public:
/* … */
};
template <typename Key, typename Value, int size>
const int AssocArray <Key, Value, size>::kSize = size;
Osservazioni su static
l Nelle classi ordinarie, static consente di avere dei
membri (inizialmente nulli per default) che hanno
medesimo valore per tutte le istanze della classe
l Il discorso si estende alle classi template,
considerando che un’istanza di classe template
equivale ad una classe ordinaria
Osservazioni su static
l Così come è possibile assegnare un valore
iniziale diverso da zero dall’esterno di un
membro static di classe ordinaria, lo stesso
discorso si estende alle classi template
l Occorre però specificare a quale particolare
istanza di classe template ci si vuole riferire.
La forma generale è:
nome-classe<tipo>::
Magazzino<Scocche> torino;
Fornitore<Francia> simca;
Uso di friend: esempio 3
l Questo esempio descrive una situazione in cui a
una particolare istanza di Magazzino (esempio
Magazzino<Scocche>) vengono fatte
corrispondere una pluralità di istanze della classe
Fornitore e della funzione Esposizione()
l Una pluralità di classi fornitore, create sulla base
della nazionalità, saranno considerate friend di
ciascun tipo di magazzino.
Keyword typename
l Il significato di “typename” (racchiuso tra <>)
all’interno di una dichiarazione template è quella
di indicare qual è il nome di tipo che è parametro
del template
l Prima della standardizzazione di typename, si
utilizzava con lo stesso significato la keyword
class (indicante al compilatore che il nome che
segue è un tipo):
Esempio:
T1 a, b;
min (a, b); // qui viene segnalato un errore se non
è stato ridefinito l’operatore ‘<‘ per il tipo T1
Vincoli impliciti
l L’errore può essere segnalato solo al momento
dell’istanziazione del template!
l Purtroppo, il linguaggio C++ segue la via dei
vincoli impliciti, ovvero non fornisce alcun
meccanismo per esplicitare assunzioni fatte sui
parametri dei template
l Tale compito è lasciato ai messaggi d’errore del
compilatore e/o ai commenti del programmatore
Template ed ereditarietà
l E’ possibile derivare una classe a partire da una
istanza di template:
Esempio: se si dichiara
Magazzino<T> m1; Magazzino<T1> m2;
Deposito<T> d1;
NON si ha relazione di discendenza tra d1 e m1,
ma solo tra Magazzino e Deposito.
In particolare, un puntatore a Magazzino<T>
NON potrà mai puntare a un Deposito<T>
Alcune osservazioni
l Ereditarietà e Template sono strumenti diversi
che condividono lo stesso fine del riutilizzo del
codice già implementato e testato
l OOP e programmazione generica sono due
diverse scuole di pensiero relative al modo di
implementare il polimorfismo (per inclusione e
parametrico, rispettivamente)
Alcune osservazioni
l Entrambi hanno pregi e difetti.
In particolare, i template soffrono dei vincoli
impliciti e della generazione di eseguibili più
grandi.
l I due strumenti non vanno comunque messi in
posizione antitetica: si può sempre rimediare ai
difetti dell’uno ricorrendo all’altro