Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
Laboratorio di Calcolo
A.A. 2013/2014
Ilaria Carlomagno
Input e Output
• Gestire la comunicazione tra utente e computer
• In C++ si usano due comandi: cin e cout.
• All’inizio del programma va incluso il pacchetto <iostream>
• Sintassi:
cout << “Questo testo verrà visto dall’utente“ <<endl;
cin >> variabile;
Esempio
comincia
#include <iostream> nuova
int main () { riga
cout<< “inserire il valore relativo a x” << endl;
cin >> x;
(…)
}
Condizione di
Simbolo
confronto
uguaglianza,
disuguaglianza
== , != Attenzione alla
maggiore, minore >,< ! differenza tra == e =
Maggiore uguale,
>= , <=
minore uguale
Condizione di
Simbolo Nome Simbolo Input
confronto
uguaglianza, AND && 2
== , !=
disuguaglianza
maggiore, minore >,< OR || 2
Hanno la precedenza
su
• Oppure...
if
Esempio:
condizione
if( a<0 ) {
vera
cout << “a è negativo“ << endl ;
}
istruzione
resto del
programma
vera
else
if( a<0 ) {
Istruzione 1 Istruzione 2
cout << “a è negativo“ << endl ;
} else {
cout << “ a è positivo o nullo “ << endl;
} resto del
programma
if( a<0 ) {
cout << “a è negativo“ << endl ;
} else if ( a==0 ) {
cout << “ a è nullo “ << endl;
} else {
cout << “ a è negativo “ << endl;
}
condizione
while ( a == 0 ) {
vera
cout << “Inserito valore nullo. Riprova.“;
cin>>a;
falsa
}
istruzione
...
vera
while
cin>>a;
} while ( a == 0 )
condizione
falsa
...
L’uso dei singoli elementi è analogo a una semplice variabile. Per stampare
l’elemento 2 del vettore, basterà utilizzare la sintassi
cout<<vettore[1];
Vantaggi:
• Accesso ai dati velocizzato
• Allocazione dinamica della memoria durante l’esecuzione
• Condivisione dei dati tra diversi blocchi
Appunti di Ilaria Carlomagno 16
I puntatori (2)
• Posso cambiare il valore della variabile “numero” in due modi:
numero = 0;
*puntatore = 0;
Il puntatore è UN indirizzo, nei vettori, ogni dato ha il proprio (indirizzo). Possiamo però sfruttare il
fatto che gli indirizzi degli elementi vettoriali sono contigui: se il primo elemento si trova
all’indirizzo «p», il successivo si troverà all’indirizzo «p+1».
Il puntatore è UN indirizzo, nei vettori, ogni dato ha il proprio (indirizzo). Possiamo però sfruttare il
fatto che gli indirizzi degli elementi vettoriali sono contigui: se il primo elemento si trova
all’indirizzo «p», il successivo si troverà all’indirizzo «p+1».
var1
var2
var3
Appunti di Ilaria Carlomagno 20
Vettori di puntatori
Posso definire un vettore di puntatori:
int vett1[5], vett2 [3], vett3 [2];
int *p[3];
*p[0] = vett1;
*p[1] = vett2;
*p[2] = vett3;
vett1
vett2
vett3
vett1[0]
vett2[0]
vett3[0]
*pp Appunti di Ilaria Carlomagno 22
Funzioni
Le funzioni permettono di strutturare i programmi in segmenti di codice che
eseguono singole istruzioni
int main () {
La funzione richiede due parametri
int z; di input: a e b.
z = somma(5,3); Sono i valori che le vengono passati
cout << “Il risultato dal main.
è" << z;
}
int main () {
int z, x, y;
z = somma(5,3);
cout << “Il risultato è" << z;
cout << “2 + 3 fa “<< somma (2,3);
cout << “ La somma di x e y inseriti fa “ << somma
(x,y);
}
Posso usarle con valori numerici, con variabili e con combinazioni dei due ( 2+x ecc.).
void stampa () {
cout << “Sono una funzione!";
}
int main () {
stampa();
}
La dichiarazione di un prototipo
richiede di specificare il tipo di
double funzione (int, double); argomenti e di return e il nome della
funzione.
double funzione (int a, double b);
Il nome delle variabili è opzionale.
Scrivere un programma che usi due funzioni void che stampino su schermo
«pari» o «dispari» a seconda del numero inserito dall’utente.
int main () {
long number = 9;
cout << number << "! = " << factorial (number);
return 0;
}
Per utilizzare l’overload il tipo di ALMENO uno dei parametri deve essere
diverso dalla precedente dichiarazione della funzione.
Appunti di Ilaria Carlomagno 31
Template di funzioni
Quando uso l’overload di una funzione per farla operare su argomenti di
tipo diverso ma con lo stesso blocco di istruzioni (corpo), posso usare il
template:
Dopo aver dichiarato la classe e creato almeno un suo oggetto, possiamo accedere e usare i
membri pubblici della classe come normali funzioni/variabili, usando la sintassi:
oggetto.metodo(parametri);
class Rettangolo{
int base, altezza;
public:
void set_values (int,int);
int area() {return base*altezza;}
};
int main () {
Rettangolo rett;
Rettangolo rett2;
rett.set_values (3,4);
rett2.set_values (10, 25);
cout<<"area1: "<<rett.area()<<endl;
cout<<"area2: "<<rett2.area()<<endl;
return 0;
}
38
Appunti di Ilaria Carlomagno
Classi (6)
Sono delle strutture di dati che comprendono sia variabili, sia operazioni su di esse. I loro
costituenti sono gli «oggetti». Se gli oggetti fossero delle variabili, potremmo pensare alla classe
come al tipo.
class Rettangolo{
int base, altezza; Il punto di forza delle classi è la
public: possibilità di definire e gestire più
void set_values (int,int); oggetti dello stesso tipo.
int area() {return base*altezza;} …Per esempio diversi rettangoli!
}; Creando un secondo rettangolo
come oggetto della classe, posso
void Rettangolo::set_values (int x, int y) {
base = x; far agire area anche su di esso…
altezza = y;
}
int main () {
Rettangolo rett;
Rettangolo rett2;
rett.set_values (3,4);
rett2.set_values (10, 25); Vediamo ora come
cout<<"area1: "<<rett.area()<<endl;
cout<<"area2: "<<rett2.area()<<endl; alleggerire il codice del
return 0; main…
}
39
Appunti di Ilaria Carlomagno
Metodi utili: stampa
class Rettangolo {
int base, altezza; Ho aggiunto alla classe Rettangolo un
public:
void set_values (int,int);
metodo che stampa base e altezza
int area() {return base*altezza;} dell’oggetto definito.
void stampa(){ In questo modo, nel main, posso
cout<<‘B= ‘<<base<<‘ H= ‘ <<
altezza<<endl;
stampare questi dati semplicemente
} invocando il metodo di stampa, senza
}; inserire il cout!
void Rettangolo::set_values (int x, int y) {
base = x;
altezza = y;
}
int main () {
Rettangolo rett;
Rettangolo rett2;
rett.set_values (3,4);
rett2.set_values (10, 25);
rett.stampa();
rett2.stampa();
return 0;
}
I due codici sono equivalenti dal punto di vista dell’esecuzione, ciò che cambia è la leggerezza del
codice: le parti «pesanti» sono state portate fuori della classe, nel riquadro a destra.
41
Appunti di Ilaria Carlomagno
Metodi utili: get
class Rettangolo { Il metodo stampa, accessibile dal main
int base, altezza; perché pubblico, può accedere agli
public:
void set_values (int,int);
attributi privati perché appartiene alla
int area() {return base*altezza;}; loro stessa classe, dunque mi consente di
int get_base () {return base;}; stampare gli attributi di Rettangolo.
void Rettangolo::set_values (int x, int y) {
base = x; Un’alternativa è l’uso di un metodo di
altezza = y; tipo get.
}
Nel codice qui a fianco, get_base()
int main () { permette a parti di programma esterne
Rettangolo rett; alla classe di accedere a valori interni e
Rettangolo rett2;
rett.set_values (3,4);
non chiede parametri in input.
cout<<“Base = “<<rett.get_base();
cout<<‘Area rett=‘<<rett.area(); Il main «vede» le funzioni non void come
return 0; variabili.
} (es.: get_base() è trattato come un
int)
I metodi che restituiscono un valore, sono visti dal main come variabili del tipo
restituito. La sintassi del cout è dunque la stessa usata per le variabili.
42
Appunti di Ilaria Carlomagno
Costruttori
Sono dei tipi particolari di funzioni che creano un oggetto appartenente alla classe. Vengono
chiamati automaticamente ogni volta che viene creato un oggetto della classe. Servono ad evitare
risultati indeterminati nel caso in cui si invochi una funzione della classe senza aver prima definito
l’oggetto su cui lavorare.
class Rettangolo{
int base, altezza; I creatori si dichiarano
public: specificando il nome e il tipo
Rettangolo (int, int); di ogni caratteristica
void set_values (int,int);
int area() {return base*altezza;}; dell’oggetto che creeranno.
}; Nell’esempio, rendono inutile
il metodo set_values.
Rettangolo::Rettangolo (int x, int y) {
base = x;
altezza = y;
}
I costruttori devono avere lo stesso nome della classe in cui sono definiti e non
hanno un tipo: nemmeno void! Questo perché i costruttori non danno output, ma
hanno la sola funzione di inizializzare gli oggetti.
43
Appunti di Ilaria Carlomagno
Costruttori
Possiamo dunque usare il costruttore dal main, come un qualsiasi altro metodo della classe.
Per definizione, il costruttore richiede due parametri che assegnerà a base e altezza,
dunque nel main scriveremo direttamente questi due valori…
class Rettangolo{
int base, altezza;
public:
Rettangolo (int, int); In una sola riga abbiamo creato
int area() {return base*altezza;} l’oggetto e abbiamo assegnato
}; un valore ai suoi attributi.
Rettangolo::Rettangolo (int x, int y) {
Prima avevamo:
base = x;
altezza = y; int main () {
} Rettangolo rett;
Rettangolo rett2;
rett.set_values (3,4);
int main () { rett2.set_values (10, 25);
Rettangolo rett (3,4); ... }
Rettangolo rett2 (10,20);
...}
A differenza delle funzioni, i costruttori possono essere chiamati solo una volta per ogni
oggetto: contestualmente alla loro creazione.
Appunti di Ilaria Carlomagno 44
Overload di Costruttori
L’overload è la definizione multipla di funzioni e vale anche per i costruttori. Ad esempio, oltre a
quello appena visto, possiamo definire un costruttore di default che viene usato quando non si
specificano base e altezza.
class Rettangolo{
int base, altezza; In questo modo, quando non
public: specifico base e altezza,
Rettangolo (); l’oggetto creato sarà un
Rettangolo (int, int);
int area() {return base*altezza;} rettangolo con base = 1 e
}; altezza = 1….un quadrato!
Rettangolo::Rettangolo (int x, int y) { int main () {
base = x; Rettangolo rett (10,6);
altezza = y; Rettangolo rett2;
} ... }
Rettangolo::Rettangolo () {
base = 1;
Quando il programma viene
altezza = 1; eseguito, viene chiamato il
} costruttore che corrisponde
alla lista di parametri inseriti.
int main () { ...}
A differenza delle funzioni, i costruttori di default vengono chiamati solo quando nessun parametro
gli viene passato…ma non vogliono le parentesi tonde vuote!
45
Appunti di Ilaria Carlomagno
Ereditarietà
E’ possibile creare delle sottoclassi a partire dalle classi già definite. Per esempio, potremmo creare la
sotto-classe Rettangolo, dalla classe dei Poligoni. Le classi figlie mantengono gli attributi delle
classi da cui provengono e possono averne degli altri.
class Poligoni {
protected:
La relazione di ereditarietà delle
int base, altezza; due classi viene dichiarata nella
public: classe derivata usando la sintassi:
void set_values (int a, int b) {base=a;
altezza=b;}
}; class classe_figlia: public
classe_madre {... };
class Rettangolo: public Poligoni {
public:
int area () {
return base*altezza;} Lo specificatore d’accesso (come al
}; solito:
int main () { private/public/protected )
Rettangolo rett; stabilisce il livello di accesso della
rett.set_values (4,5); classe figlia.
cout<<rett.area()<<'\n';
return 0; Membri protected sono visibili alle
} classi figlie, membri privati no.
I membri ereditati mantengono la stessa visibilità per gli oggetti esterni alla classe.
46
Appunti di Ilaria Carlomagno
Ereditarietà (2)
Lo specificatore denota il livello più alto accessibile che i membri ereditano dalla classe che lo
segue (nell’esempio Poligono) avranno dalla classe ereditata (in questo caso, Rettangolo). Poiché
public è il livello più alto accessibile, la classe derivata definita con questo specificatore erediterà
tutti i membri con gli stessi livelli della classe da cui deriva.
Con protected, i membri pubblici sono ereditati come protetti, nella classe derivata.
Con private, tutti i membri della classe madre sono ereditati come privati.
47
Appunti di Ilaria Carlomagno
Membri Virtuali (Polimorfismo)
Un membro virtuale è una funzione membro che può essere ridefinita in una classe derivata,
preservando le sue proprietà di chiamata tramite referenza
referenza. La sintassi per una funzione derivata
prevede di farne preseguire la dichiarazione con virtual:
int main () {
class Poligoni { Rectangolo rett;
protected: int base, altezza; Triangolo triang;
public: void set_values (int a, int b) { Poligoni poly;
base = a; altezza = b; } Poligoni * ppoly1 = &rett;
virtual int area () { return 0; } Poligoni * ppoly2 = &triang;
}; Poligoni * ppoly3 = &poly;
class Rettangolo: public Poligoni { ppoly1->set_values (4,5);
public: int area () { return base * altezza; } ppoly2->set_values (4,5);
}; ppoly3->set_values (4,5);
class Triangolo: public Poligoni { cout << ppoly1->area() << '\n';
public: int area () { return (base* altezza / cout << ppoly2->area() << '\n‘;
2); } cout << ppoly3->area() << '\n‘;
}; return 0;
}
Ricordate la sintassi dei puntatori: &variabile indica l’indirizzo fisico in cui è salvata
l’informazione. Il dato è accessibile con la sintassi *puntatore.
48
Appunti di Ilaria Carlomagno
Membri Virtuali (Polimorfismo)-(2)
int main () {
class Poligoni { Rectangolo rect;
protected: int base, altezza; Triangolo trgl;
public: void set_values (int a, int b) { Poligoni poly;
base = a; altezza = b; } Poligoni * ppoly1 = ▭
virtual int area () { return 0; } Poligoni * ppoly2 = &trgl;
}; Poligoni * ppoly3 = &poly;
class Rettangolo: public Poligoni { ppoly1->set_values (4,5);
public: int area () { return base * altezza; } ppoly2->set_values (4,5);
}; ppoly3->set_values (4,5);
class Triangolo: public Poligoni { cout << ppoly1->area() << '\n';
public: int area () { return (base* altezza/2); cout << ppoly2->area() << '\n‘;
} cout << ppoly3->area() << '\n‘;
}; return 0;
}
Il metodo area() è stato dichiarato virtual nella classe madre perché è ridefinito nelle classi
figlie. I membri non-virtuali possono essere anch’essi ridefiniti nelle classi derivate ma l’accesso a
tali membri non può avvenire tramite puntatori della classe madre: se virtual fosse rimosso dalla
dichiarazione, tutte le tre chiamate restituirebbero zero perché verrebbe chiamata al loro posto la
versione della classe madre.
In poche parole, virtual permette ai membri di una classe figlia aventi lo stesso nome di uno
della classe madre di essere opportunamente chiamati da puntatori appartenenti alla classe madre
che puntano a un oggetto della classe figlia.
Class B: public A {
int b;
public:
B(){b = 2;}
virtual void show(){cout <<b;}
};
Cosa succede se eseguo
int main() {
A *pA;
questo codice?
B oB; Quale numero vedrò sul
pA = &oB; terminale?
pA->show();
return 0;
}
50
Appunti di Ilaria Carlomagno
Esempio
Vediamo, riga Class A { Sto
int a;
per riga, cosa public:
applicando un
sto facendo… A() {a = 1;} metodo ad un
virtual void show(){cout <<a;} puntatore
}; della classe
Class B: public A { madre (pA)
int b; che punta ad
public: un oggetto
B(){b = 2;}
void show(){cout <<b;}
della classe
}; figlia (oB).
Il metodo
int main() { usato sarà
A *pA; //dichiaro puntatore di A
B oB; //dichiaro oggetto di B dunque
pA = &oB; //faccio puntare pA a oB quello della
pA->show(); //applico metodo a pA classe figlia e
return 0;
}
sul terminale
vedrò «2».
51
Appunti di Ilaria Carlomagno
*.h ; *.c e *.cpp
class Rettangolo {
int base, altezza; Nell’header (*.h) vanno inclusi solo i
public:
void set_values (int,int);
prototipi (le dichiarazioni) dei metodi.
int area() {return base*altezza;}
void stampa(){
cout<<“B= ” <<base<< “H= ” << Nel *.cc va specificato il corpo di ciascun
altezza<<endl;
}
metodo.
};
52
Appunti di Ilaria Carlomagno
*.h ; *.c e *.cpp (2)
class Rettangolo {
int base, altezza; Per vedere meglio la divisione, scriviamo
public:
la classe con le sole dichiarazioni dei
void set_values (int,int);
int area(); metodi:
void stampa();
};
Nell’header (*.h) vanno inclusi solo i
int Rettangolo::area() {return base*altezza;}; prototipi (le dichiarazioni) dei metodi.
void Rettangolo::stampa() {
cout<<“B= ” <<base<< “H= ” << Nel *.cc va specificato il corpo di ciascun
altezza<<endl; metodo.
};
53
Appunti di Ilaria Carlomagno
*.h ; *.c e *.cpp (3)
class Rettangolo {
int base, altezza;
public: Nell’header (rett.h) vanno inclusi solo i
void set_values (int,int); prototipi (le «dichiarazioni») dei metodi.
int area();
void stampa();
}; #ifndef RETT_H
#define RETT_H
int Rettangolo::area() {return base*altezza;};
class Rettangolo {
void Rettangolo::stampa() { int base, altezza;
cout<<“B= ” <<base<< “H= ” << public:
altezza<<endl; void set_values (int, int);
}; int area();
void stampa();
void Rettangolo::set_values (int x, int y) { };
base = x;
altezza = y; #endif
};
int main () {
Rettangolo rett;
Rettangolo rett2;
rett.set_values (3,4);
rett2.set_values (10, 25);
rett.stampa();
rett2.stampa();
return 0;
}
54
Appunti di Ilaria Carlomagno
*.h ; *.c e *.cpp (4)
class Rettangolo {
int base, altezza; Nel rett.cc va specificato il corpo di
public:
ciascun metodo (implementazione).
void set_values (int,int);
int area();
void stampa(); #include “rett.h”
}; #include <iostream>
55
Appunti di Ilaria Carlomagno
*.h ; *.c e *.cpp (5)
class Rettangolo {
int base, altezza;
public: Nel prog.cpp va messo il main e tutte le
void set_values (int,int); istruzioni che vogliamo far eseguire al
int area();
void stampa();
nostro programma.
}; Ovviamente dovremmo includere header
e cc.
int Rettangolo::area() {return base*altezza;};
56
Appunti di Ilaria Carlomagno
class Rettangolo {
public:
int base, altezza;
#ifndef RETT_H
#include “rett.h” #define RETT_H
#include <iostream>
class Rettangolo {
int Rettangolo::area() {return base*altezza;}; int base, altezza;
public:
void Rettangolo::stampa() { void set_values (int, int);
cout<<“B= ” <<base<< “H= ”<<altezza<<endl;}; int area();
void stampa();
void Rettangolo::set_values (int x, int y) { };
base = x;
altezza = y;}; #endif
rett.cc rett.h 57
Appunti di Ilaria Carlomagno
Compilazione #include “rett.cc”
#include <iostream>
int main () {
Abbiamo diviso il nostro codice in tre parti… Rettangolo rett;
Se compilassimo solo il programma.cpp, Rettangolo rett2;
rett.set_values (3,4);
otterremmo degli errori! rett2.set_values (10, 25);
rett.stampa();
rett2.stampa();
#include “rett.h” return 0;
#include <iostream> }
#endif
g++ -c rett.cc
g++ -c programma.cpp
g++ programma.o rett.o – o Prova