Come detto nell’introduzione della guida al C, il C++ nasce come estensione del C, in particolare esso aggiunge una serie di funzionalità che lo rendono un ottimo strumento per la programmazione orientata agli oggetti.
Gli oggetti non sono altro che componenti software riutilizzabili modellati sul mondo reale. Programmare seguendo il paradigma OO (Object Oriented) rende più semplice la lettura del codice, la sua correzione e la sua modifica.
Lo scopo principale di questa guida è quello di fornire una visione generale del linguaggio C++ e del paradigma OO, tuttavia, prima di entrare nel merito, ritego opportuno formulare una piccola ma doverosa premessa:
Poco sopra abbiamo detto che il C++ è un’evoluzione del C, capirete quindi che esso eredita gran parte dello stile di programmazione del suo genitore. Si rimanda quindi il lettore alla guida al C per imparare i fondamenti della programmazione di questo linguaggio, tra i quali soprattutto:
- i tipi di variabili (cap 4)
- le istruzioni condizionali (cap 5)
- le istruzioni iterative (cap 7)
- la strutturazione in funzioni (cap 6)
In generale una buona lettura della guida al C rende più facilmente comprensibile i codici presentati in questa guida. Quindi se non conoscete il C e non avete ancora letto la sua guida… fatelo subito!
Editor e ambiente di sviluppo
Per il C++ utilizzeremo il compilatore freeware Dev-C++.
Per utilizzare a pieno le potenzialità del paradigma OO bisognerà utilizzare il compilatore in un’altra maniera, creando dei progetti. La cosa è abbastanza intuitiva:
Clicchiamo sull’icona nuovo progetto
e selezioniamo l’icona empty project…
A questo punto diamo un nome al nostro progetto ed inseriamo un nuovo file sorgente cliccando su
, salviamo tale file sorgente dandogli il nome main (in quanto lì andrà scritto il nostro programma principale) e scriviamo nella finestra bianca il seguente codice per verificare il corretto funzionamento:
#include <iostream>
using namespace std;
int main()
{
cout << "Benvenuti nella guida al C++" << endl;
system("PAUSE");
return 0;
}
Il vostro compilatore dovrebbe apparire così:
Ricordo che per compilare un progetto bisogna cliccare su una delle seguenti icone
il cui significato è spiegato sempre nel cap. 2 della guida al C presente su questo sito.
Nella colonna a sinistra verranno visualizzati tutti i file inclusi nel progetto, a destra il codice del file selezionato.
Se dopo la compilazione, se tutto è andato a buon fine, dovreste avere in output la seguente schermata:
Lo streaming di input/output
Chi ha letto la guida al C, leggendo il codice proposto per verificare il funzionamento del compilatore, avrà notato che vi è un diverso modo di chiamare una stampa a video. Infatti con il C++ si abbandonano le funzioni printf() e scanf() e tutta la libreria stdio.h a vantaggio dei nuovi operatori di streaming:
- << (appartenente alla classe cout)
- >> (appartenente alla classe cin)
Questi operatori sono contenuti all’interno della librerie iostream che dovrà essere inclusa nel nostro codice come al solito.
Grazie allo streaming si risolvono tutte le problematiche dovute all’uso di specificatori all’interno delle funzioni printf() e scanf() in quanto sarà il compilatore stesso a farsi carico dell’assegnazione del giusto tipo ad una data variabile. Facciamo un esempio:
// Controlla se due numeri sono multipli
#include <iostream>
//usa lo spazio dei nomi standard
using namespace std;
//prototipo della funzione
void multiple (int, int ) ;
int main()
{
//dichiarazione di due interi
int a, b;
cout << "Inserisci due interi:" << endl;
//acquisizione di due interi
cin >> a >> b;
//invocazione della funzione
multiple (a,b);
system("PAUSE");
return 0;
}
//dichiarazione della funzione
void multiple (int x, int y )
{
//dichiarazione di una variabile boolenana
bool risultato;
//due if innestati per stabilire se x è o meno
//multiplo di y
if (x >= y )
{
risultato = x%y ;
if ( risultato == 0 )
cout << x << " e' multiplo di " << y << endl;
else
cout << x << " non e' multiplo di " << y << endl;
}
if (x < y )
{
risultato = y%x;
if ( risultato == 0 )
cout << y << " e' multiplo di " << x << endl;
else
cout << y << " non e' multiplo di " << x << endl;
}
}
Analizziamo il codice: tale programma non fa altro che controllare se un numero è multiplo di un altro utilizzando l’operatore % che indica il resto di una divisione, quindi se un numero è multiplo di un altro allora il resto sarà pari a zero.
using namespace std: indica che si intende usare lo spazio dei nomi standard. Questa è una delle nuove caratteristiche del C++ che permette di evitare conflitti con i vari header file che si andranno a creare, in particolare questa istruzione indica che si adopereranno librerie standard del C++ e non librerie proprie.
void multiple (int, int ); è il prototipo della funzione che deve calcolare se i numeri sono multipli; essa non deve restituire nulla quindi è dichiarata void, notiamo che essa accetta in ingresso due parametri di tipo intero.
All’interno del main vengono poi dichiarati due interi a e b i quali ci serviranno per le operazioni di input.
cout << "Inserisci due interi:" << endl; – cout significa che vogliamo mandare in output a video ciò che segue il simbolo << in questo caso ciò che è riportato tra doppi apici "……" viene stampato a video interpretato come una stringa di caratteri, invece endl sta per vai a capo (cosa che in C facevamo usando n).
cin >> a >> b; – cin indica che vogliamo acquisire un dato da tastiera e memorizzarlo nella variabile indicata dopo il simbolo >> nel nostro caso prima la variabile a poi quella b. Notiamo come non è più necessario specificare che quelli in input sono dei dati interi %d come avviene nel C, sarà il compilatore stesso ad interpretare per noi tale dato.
multiple (a,b); – come al solito è il modo in cui si richiama una funzione, in questo caso gli passiamo i parametri a e b.
return 0; – come nel C indica che il main è terminato correttamente.
void multiple (int x, int y ) – indica che da qui in poi vi è la dichiarazione della funzione multiple che non restituirà nulla e i cui valori di input di tipo intero saranno chiamati x e y.
Segue un if in cui si entra nel caso in cui x è maggiore o uguale di y; a questo punto si effettua l’istruzione risultato = x%y in cui si calcola il resto di x per y. Segue un if…else dove viene stampata la frase "x è multiplo di y" oppure "x non è multiplo di y" a seconda del valore contenuto in risultato.
Nota: anche il cout non ha più bisogno di specificatori di tipo basta porre nello streaming il dato che si vuole quando lo si vuole mandare in stampa cosi facendo l’istruzione cout << x << " e’ multiplo di " << y << endl; produrrà in output video la seguente frase supposto x = 30 e y = 5 30 è multiplo di 5.
L’altro if viene usato quando x è minore di y per invertire l’ordine di divisore e dividendo per ottenere il resto fra i due.
E’ possibile effettuare un casting (cambiare il tipo della variabile) sui dati mandati in output direttamente nello streaming. Per fare ciò basta usare delle parentesi tonde che contengano il tipo in cui si vuole trasformare il nostro dato nel seguente modo:
#include <iostream>
using namespace std;
float x;
char a;
int main()
{
cout << "Immetti un numero reale (float) ";
cin >> x ;
cout << "Immetti un carattere (char)";
cin >> a;
cout << "Il numero immesso e' " << x
<< " convertito in intero e' " << (int) x
<< endl;
cout << "Il carattere immesso e' " << a
<< " il suo valore ASCII e' " << (int) a
<< endl;
system("PAUSE");
}
L’esecuzione sarà la seguente:
Immetti un numero reale (float) 3.999
Immetti un carattere (char) g
Il numero immesso e’ 3.999 convertito in intero e’ 3
Il carattere immesso e’ g il suo valore ASCII e’ 103
Premere un tasto per continuare . . .
Nell’output i valori immessi dall’utente sono stati scritti in grassetto quelli trasformati dopo il cast in corsivo.
La strutura di selezione Switch
Il costrutto switch serve per effettuare decisioni multiple sulla base di una variabile. Esso prevede varie etichette case più una opzionale default.
Facciamo subito un esempio: poniamo di voler contare i voti immessi secondo i parametri di giudizio da A (voto più alto) a F (voto più basso), permettendo di inserire i voti fino a che non viene premuto il carattere EOF (sotto windows si ottiene premendo CTRL+Z sotto UNIX CTRL+D).
//Uso dello switch
#include <iostream>
using namespace std;
int main()
{
int voto, //voto generico
aCount = 0, //occorrenze di a
bCount = 0, //occorrenze di b
cCount = 0, //occorrenze di c
dCount = 0, //occorrenze di d
eCount = 0, //occorrenze di e
fCount = 0; //occorrenze di f
cout << "Inserisci la lettera corrispondente al voto."
<< endl
<< "Premi CTRL+Z per smettere di immettere voti."
<< endl;
while (( voto = cin.get() ) != EOF )
{
switch (voto)
{
case 'A': //voto A maiuscola
case 'a': //voto a minuscola
++aCount; // incrementa le a
break; //esce dallo switch
case 'B': //voto B maiuscola
case 'b': //voto b minuscola
++bCount; // incrementa le b
break; //esce dallo switch
case 'C': //voto C maiuscola
case 'c': //voto c minuscola
++cCount; // incrementa le c
break; //esce dallo switch
case 'D': //voto D maiuscola
case 'd': //voto d minuscola
++dCount; // incrementa le d
break; //esce dallo switch
case 'E': //voto E maiuscola
case 'e': //voto e minuscola
++eCount; // incrementa le e
break; //esce dallo switch
case 'F': //voto F maiuscola
case 'f': //voto f minuscola
++fCount; // incrementa le f
break; //esce dallo switch
case 'n': //ignora nuova linea
case 't': //ignora tabulazoni
case ' ': //ignora spazi vuoti
break; //esce dallo switch
default: //tutti gli altri caratteri
cout << "Voto non consentito."
<< "Immettere un nuovo voto." << endl;
break;
}
}
cout << endl << endl << "Occorrenze di tutti i voti:"
<< endl << "A: " << aCount
<< endl << "B: " << bCount
<< endl << "C: " << cCount
<< endl << "D: " << dCount
<< endl << "E: " << eCount
<< endl << "F: " << fCount << endl;
system("PAUSE");
return 0;
}
Lo switch quindi non fa altro che eseguire l’istruzione ++xCount dove x rappresenta il carattere digitato nel caso in cui esso sia ammissibile altrimenti come descritto nel caso di default viene richiesto di inserire un nuovo voto.
while (( voto = cin.get() ) != EOF ) – questa istruzione sta a significare "ripeti tutto (cioè la struttura switch con i vari case) fino a che non viene immesso EOF".
Per il resto leggendo i commenti contenuti nel codice è facile capire come funziona il costrutto switch.
Nota: la funzione cin.get() non fa altro che leggere un singolo carattere da tastiera e porlo nella variabile voto.
Puntatori e stringhe
A differenza delle variabili viste fino ad ora, le quali contengono un valore specifico, il valore di una variabile di tipo puntatore è una locazione di memoria; o meglio un puntatore contiene l’indirizzo di un’altra variabile che a sua volta contiene un valore specifico. Questo è uno degli argomenti più ostici della programmazione in C++!!!
Come tutte le altre variabili anche i puntatori hanno bisogno di essere dichiarati, tale dichiarazione va effettuata nella seguente maniera:
tipo_puntatore *nomePuntatore;
è quindi il simbolo * ad identificare un puntatore. La dichiarazione può essere interpretata come nomePuntatore è un puntatore ad una variabile di tipo tipoPuntatore.
Dobbiamo inoltre inizializzare un puntatore; se nella dichiarazione ciò non viene fatto esso assume il valore NULL oppure 0 che corrisponde a non punta a nulla. Facciamo un esempio per spiegare l’uso dei puntatori e il loro uso:
//Uso dei puntatori
#include <iostream>
using namespace std;
int main()
{
int a; //a è un intero
int *aPtr; //aPtr è un puntatore ad a
a = 7; //assegniamo ad a il valore 7
aPtr = &a; //assegniamo ad aPtr l'indirizzo di a
cout << "L'indirizzo di a e': " << &a
<< "nIl valore di aPtr e': " << aPtr;
cout << "nnIl valore di a e': " << a
<< "nIl valore puntato da *aPtr e' " << *aPtr
<< endl<<endl;
system("PAUSE");
return 0;
}
L’operatore & è un operatore unario esso restituisce l’indirizzo del suo operando.
L’operatore * è detto operatore di risoluzione del riferimento esso restituisce una copia dell’oggetto a cui punta il suo operando. Quindi quando si dereferenzia un puntatore esso si può utilizzare come se fosse la variabile a cui punta.
Nota: in realtà il nome di un’array non è altro che il puntatore al suo primo elemento!
Abbiamo visto che in C la trattazione di una stringa non era un argomento molto semplice… il C++, con l’uso dei puntatori ed una libreria apposita, semplifica molto la trattazione delle stringhe grazie a funzioni di copia e acquisizione di stringhe, inoltre la stampa è resa più agevole dall’uso dello streaming.
Facciamo ora degli esempi per spiegare l’utilizzo delle funzioni della libreria string.h.
//utilizzo delle funzioni
//strcpy e strncpy
#include <iostream>
#include <string.h>
using namespace std;
int main()
{
//dichiarazione della stringa x
char x[] = "Guida al C++ di MrWebMaster";
//dichiarazione di y e z con relative dimensioni
char y[28], z[13];
cout << "La stringa contenuta nell'array x e':tt"
<< x
<< "nnCopiamo la stringa x in quella y con strcpy: t"
//effettuiamo la copia di x in y
<< strcpy(y,x) << endl << endl;
//copiamo i primi 12 caratteri di x in z
strncpy(z, x, 13);
//aggiungiamo il carattere terminatore
z[13] = ' ';
cout << "Copia dei primi 12 caratteri di x in z: "
<< z << endl;
system("PAUSE");
return 0;
}
strcpy(y,x) – copia ad uno ad uno i caratteri contenuti in x e li pone in y; se y non è abbastanza grande da contenere x allora tronca la stringa.
strncpy(z,x,13) – copia ad uno ad uno i primi 13 caratteri di x e li pone in z. Logicamente non riesce a copiare il carattere terminatore che va copiato a mano con l’istruzione z[13] = ‘