Dal C al C++: differenze tra le versioni

Contenuto cancellato Contenuto aggiunto
Nessun oggetto della modifica
Nessun oggetto della modifica
Riga 393:
* Evidenziare errori di programmazione. Se una variabile con deve essere modificata e invece lo è, si tratta di un errore logico che verrà segnalato dal compilatore.
* Rendere il codice più efficiente. Se il compilatore sa che una variabile ha un valore costante, può propagare il suo valore ovunque è usata la variabile. Per variabili locali, questo viene già fatto dai compilatori ottimizzanti, ma normalmente non viene fatto per le variabili globali. Questo fatto rende inutile e sconsigliato l'uso delle macro del preprocessore per definire costanti.
 
== L'uso dei template ==
In C++ si possono definire non solo classi e funzioni, ma anche template di classe e template di funzione.
 
Per esempio, nella libreria standard è definito il template di classe "vector", che è un'array dinamico.
Tale template di classe non definisce il tipo degli elementi contenuti nell'array.
Tale tipo è un parametro del template.
Per poter utilizzare il template si deve specificare il tipo degli elementi.
Vediamo qualche esempio:
vector a; // Illegale, parametro non specificato
vector<int> b; // Array dinamico di int
vector<int> c; // Altro array dinamico di int
b = c; // Copia un vector su un altro dello stesso tipo
vector<double> d; // Array dinamico di double
b = d; // Illegale, non si possono copiare array di double su array di int
La parola "vector" rappresenta la definizione di una classe generica, definita a meno di un parametro;
non è una classe, ma una famiglia parametrica di classi, e quindi non si può usare direttamente per dichiarare o definire una variabile o una funzione.
 
Se dopo la parola "vector" si pone tra parentesi angolari un'espressione che identifica un tipo, si ottiene un'''istanza'' del template, che è un tipo, utilizzabile come qualunque altro tipo.
 
L'istanziazione di un template di classe, cioè il passaggio dal template al tipo, viene eseguito in fase di compilazione, e pertanto il parametro deve essere una definito in fase di compilazione.
 
Un template può ammettere più parametri, e questi parametri non sono necessariamente dei tipi, in quanto sono ammessi come parametri anche numeri interi.
Consideriamo il seguente programma:
#include <iostream>
#include <bitset>
using namespace std;
int main() {
bitset<6> a;
a[0] = 1;
a[2] = 1;
cout << a;
}
La prima riga della funzione "main" è la dichiarazione della variabile "a", di tipo "bitset<6>".
Il template di classe "bitset" fa parte della libreria standard, ed è definito nel file di intestazione "bitset".
 
Questo template di classe rappresenta una sequenza di bit di lunghezza fissa, e richiede un parametro di tipo intero che indica il numero di bit contenuti nell'oggetto.
Nel nostro esempio, "a" è una sequenza di sei bit.
L'oggetto "a" non è inizializzato esplicitamente, ma la classe bitset<6> lo inizializza implicitamente impostando a zero tutti i suoi bit.
Le due istruzioni successive impostano a 1 il primo e il terzo bit, contando da zero, cioè quelli di indice zero e di indice due.
L'ultima istruzione emette sulla console una rappresentazione testuale della variabile.
Il risultato sarà la stampa della stringa "000101".
 
Chiaramente non si possono mescolare tipi con valori interi.
Nella definizione di "vector" è specificato che il parametro deve essere un tipo, mentre nella definizione di "bitset" è specificato che il parametro deve essere un intero.
Le seguenti due righe producono errori di compilazione:
vector<4> a; // Illegale, vector richiede un tipo
bitset<double> b; // Illegale, bitset richiede un intero
 
In C++, oltre ai template di classe esistono i template di funzione.
Ecco un esempio:
#include <iostream>
#include <string>
using namespace std;
int main() {
double a = max<double>(2.3, 6.8);
string b = min<string>(string("abc"), string("xyz"));
cout << a << " " << b;
}
Questo codice utilizza due template di funzione "max", che rende il valore maggiore tra i due suoi parametri, e "min", che rende il valore minore tra i suoi due argomenti.
 
Il programma stampa su console la stringa "6.8 abc", infatti il numero double "6.8" è maggiore del numero double "2.3", e la stringa "abc" è minore della stringa "xyz" in ordine alfabetico.
 
In modo analogo ai template di classe, anche questi template di funzione sono stati istanziati specificando tra parentesi angolari il parametro del template, che per questre funzioni è il tipo dei parametri della funzione.
Se si specifica un tipo e si passa un tipo diverso, si ottiene un errore sintattico.
Per esempio:
double a = max<double>(string("abc"), string("xyz")); // Illegale
string b = min<string>(2.3, 6.8); // Illegale
Nella prima riga si passano due stringhe alla funzione "max<double>", che si attende due numeri;
nella seconda riga si passano due numeri alla funzione "min<string>", che si attende due stringhe.
 
A differenza dei template di classe, i template di funzione possono sottintendere il parametro del template.
Per esempio, il seguente programma è equivalente al precedente:
#include <iostream>
#include <string>
using namespace std;
int main() {
double a = max(2.3, 6.8);
string b = min("abc", "xyz");
cout << a << " " << b;
}
Siccome al template di funzione "max" vengono passati come parametri di funzione due valori entrambi di tipo "double", il compilatore deduce che il tipo del template è "double", e istanzia la funzione "max<double>".
Analogamente, Siccome al template di funzione "min" vengono passati come parametri di funzione due valori entrambi di tipo "string", il compilatore deduce che il tipo del template è "string", e istanzia la funzione "min<string>".
 
Tale inferenza automatica permette spesso al programmatore di scrivere meno codice.
Tuttavia a volte l'inferenza non è possibile, in quanto c'è un'ambiguità.
Per esempio:
double a = min(2, 3.4); // Illegale
In questa riga il compilatore non sa se istanziare la funzione "min<double>" o "min<int>" e quindi si arrende.
L'uso che viene fatto del valore reso da una funzione non viene mai utilizzato per inferire il parametro di un template, quindi il fatto che il valore sia usato per inizializzare una variabile di tipo "double" è ininfluente.
Per risolvere l'ambiguità si può usare una delle quattro righe seguenti:
double a = min<double>(2, 3.4);
double a = min(2., 3.4);
double a = min((double)2, 3.4);
double a = min(double(2), 3.4);
La prima versione specifica esplicitamente il parametro del template, generando una funzione che si attende due valori double.
A questo punto, il parametro "2" viene automaticamente convertito in "double", come avviene quando in linguaggio C si passa un valore intero a una funzione che si attende un parametro di tipo "double".
 
Le altre tre versioni convertono il valore "2" nel corrispondente valore "double".
la seconda e la terza sono valide anche in linguaggio C.
La quarta versione usa una sintassi di type-cast introdotto dal C++, e equivalente a quella della terza versione.
 
{{wikipedia|titolopedia=C plus plus|titolobooks=il linguaggio C++}}