www.hoepli.it
ISBN 978-88-203-4248-7
Ristampa:
Realizzazione editoriale
ART Servizi Editoriali S.p.A. - Bologna
www.art.bo.it
Coordinamento editoriale: Monica Monari
Redazione: Barbara Megali
Progetto grafico: Marina Baldisserri
Impaginazione: Sonia Bertusi
Stampa: Art Grafiche Franco Battaia S.r.l. - Zibido San Giacomo (MI)
Printed in Italy
Indice
3
Indice
4
Presentazione
Il presente volume espone, in modo chiaro ed efficace, le caratteristiche del linguaggio C++
e ha il duplice scopo di descriverne la sintassi e di evidenziarne le potenzialità.
In particolare il libro:
Il primo obiettivo si realizza tramite numerosi esempi presenti nel testo, che forniscono
chiare indicazioni sulle caratteristiche sintattiche del linguaggio. Per quanto riguarda le
basi teoriche sono stati messi in rilievo i fondamenti dei cinque argomenti di base per la
programmazione: la rappresentazione dei dati, le strutture di controllo utilizzabili nella
costruzione di un algoritmo, le strutture dei dati, la programmazione orientata agli og-
getti e la gestione dei file.
1 La sezione Premesse sviluppa gli argomenti della codifica binaria delle informazioni.
2 In Primi elementi di programmazione vengono descritti i concetti di variabile,
costante e tipi di dato, le operazioni di input/output da console e gli operatori arit-
metici.
3 Nella sezione Organizzazione degli algoritmi viene introdotta la nozione di algorit-
mo e sono descritte le principali strutture di controllo sia in pseudocodifica sia in C++.
4 Nella sezione Strutture dei dati vengono definite le principali strutture statiche dei
dati.
5 L’intera sezione Classi e oggetti è dedicata alle nozioni fondamentali della OOP e
vengono presentati i concetti principali della programmazione orientata agli ogget-
ti quali l’incapsulamento, il polimorfismo e l’ereditarietà.
6 La sezione Operare con gli archivi spiega le nozioni di base per la definizione de-
gli archivi di dati.
7 La sezione Le eccezioni descrive gli accorgimenti essenziali per la realizzazione di
applicazioni “robuste”.
5
Presentazione
di facile lettura, che aiuta lo studente a concentrarsi, di volta in volta, su un singolo ele-
mento del discorso. Tutti i concetti presentati sono accompagnati da un esempio, che
mette in pratica quanto esposto. Ogni esempio contiene un listato di codice, una figura
che illustra una prova di esecuzione del codice proposto e lÕanalisi dettagliata del codice
stesso; questÕultima parte dellÕesempio presenta una descrizione dettagliata degli aspetti
pi• significativi del linguaggio C++ presenti nellÕesempio.
Per ogni sezione sono indicati gli obiettivi generali che si vogliono raggiungere, mentre
nella prima pagina di ogni Unitˆ didattica • specificato per lo studente ÒChe cosa impare-
rai a fareÓ e ÒChe cosa dovrai studiareÓ. In concreto, gli obiettivi generali presentati allÕi-
nizio di ogni modulo descrivono le capacitˆ che lo studente deve acquisire. Le voci ÒChe
cosa imparerai a fareÓ e ÒChe cosa dovrai studiareÓ indicano rispettivamente le compe-
tenze e le conoscenze che devono essere apprese dallÕalunno.
Considerando lÕampiezza della trattazione, il libro include tutti i contenuti dei program-
mi didattici tradizionalmente affrontati nelle classi terze degli istituti tecnici.
In particolare pu˜ essere adottato nella classe terza degli Istituti Tecnici Industriali con in-
dirizzo ABACUS o informatica industriale, in quella degli Istituti Tecnici Commerciali
con indirizzo MERCURIO o programmatori, nonchŽ in quella dei Licei scientifici tec-
nologici.
LÕAutore
6
Sezione 1
Premesse
†
Obiettivi ◊ Nozioni fondamentali sui sistemi di numerazione
◊ Sistemi di numerazione in base diversa da 10
◊ Introduzione alla codifica delle informazioni
Da quanto convenuto, segue che ogni numero contiene un certo numero di unitˆ semplici, un certo
numero di decine, un certo numero di centinaia ecc., pertanto ogni numero naturale pu˜ essere
scritto come una somma i cui termini sono prodotti di una potenza di dieci per una opportuna cifra.
Esempio..............................................................................................................................
Indicando, per ora, con la lettera d il numero dieci, si ha:
novemilatrecentoquarantasette =
9 migliaia + 3 centinaia + 4 decine + 7 unitˆ =
9 × d3 + 3 × d2 + 4 × d1 + 7 × d0
La scrittura di queste cifre, una dopo lÕaltra, costituisce la scrittura posizionale, in base 10, del nu-
mero dato.
In tal modo, il numero ÒnovemilatrecentoquarantasetteÓ si scrive:
9347
...........................................................................................................................................
9
Sezione 1 - Premesse
Ogni cifra di un numero naturale ha un valore intrinseco, che • quello che ha in quanto numero, e un valo-
re che dipende dalla posizione che occupa nel numero.
Per esempio, nel numero:
444
i tre Ò4Ó hanno lo stesso valore intrinseco come cifre singole, ma valori diversi in base alla posizione che
occupano nel numero: infatti, il primo Ò4Ó da destra rappresenta quattro unitˆ semplici, il secondo rap-
presenta quattro decine e il terzo quattro centinaia.
Le cifre, quindi, acquistano maggior significato da destra a sinistra. Per questo motivo la prima cifra a de-
stra • la Òmeno significativaÓ e la prima cifra a sinistra • la Òpi• significativaÓ.
0,1 = 10Ð1
0,01 = 10Ð2
ÉÉÉÉÉÉ
ÉÉÉÉÉÉ
0,00É1 = 10Ðn
{
n cifre decimali
Esempio..............................................................................................................................
Il numero
N = 7 × 103 + 3 × 10 + 2 × 10−1 + 3 × 10−3
scritto nella forma simbolica •:
N = 7030.203
...........................................................................................................................................
La scelta della base per la scrittura dei numeri • determinata soltanto da ragioni di comodo; un nu-
mero pu˜ essere scritto in una base qualsiasi senza che il suo intrinseco valore cambi perchŽ tale va-
lore • qualcosa che esiste realmente, indipendentemente dal sistema numerico usato. Per esempio,
un sacchetto di caramelle ne contiene una certa quantitˆ e questa quantitˆ • sempre la stessa, qua-
lunque sia il sistema numerico usato per esprimerla. Pi• in generale, possiamo dire che in un sistema
numerico posizionale in base b (con b > 1) un numero naturale pu˜ essere cos“ rappresentato:
Nb = an × bn + an−1 × bn−1 + ... + a1 × b + a0 × b0
Conveniamo di indicare sempre la base, se questa non • 10. Quindi i simboli N2, N5, N rappresen-
tano numeri in base 2, 5, 10, rispettivamente.
10
Unitˆ didattica 1 - Sistemi di numerazione
Il sistema binario • il pi• semplice sistema di numerazione che si possa considerare, perchŽ • quello
che richiede il minor numero di simboli per esprimere graficamente tutti i numeri. Anche in questo
sistema il valore delle cifre dipende dalla posizione da esse occupata rispetto al punto radice.
Spostando una cifra di una posizione verso sinistra, si moltiplica il suo valore per 2, mentre spostan-
dola verso destra si divide il suo valore per 2. Quindi, anche i numeri binari si possono scrivere nella
forma polinomiale:
N2 = an × 2n + an−1 × 2n−1 + ... + a1 × 2 + a0 × 20 + a−1 × 2−1 + a−2 × 2−2 + ...
e la scrittura simbolica di N2 •:
N2 = anan−1 ... a1a0a−1a−2 ...
11
Sezione 1 - Premesse
Esempi ................................................................................................................................
1. 1101112 = 1 × 25 + 1 × 24 + 0 × 23 + 1 × 22 + 1 × 2 + 1 = 55
per cui 110111 scritto in base 2 equivale a 55 scritto in base 10.
2. 101.112 = 1 × 22 + 0 × 2 + 1 + 1 × 2Ð1 + 1 × 2Ð2 = 4 + 1 + 1/2 + 1/4 = 5.75
per cui 101.11 scritto in base 2 equivale a 5.75 scritto in base 10.
...........................................................................................................................................
Esempio..............................................................................................................................
Convertire il numero 35 in binario.
35 : 2 = 17 resto 1 1
17 : 2 = 8 resto 1 1
8:2= 4 resto 0 0
4:2= 2 resto 0 0
2:2= 1 resto 0 0
1:2= 0 resto 1 1
Cifre binarie: 1 0 0 0 1 1
Pertanto il numero 35, in base 2 • 100011.
...........................................................................................................................................
Esempio..............................................................................................................................
Convertire in base 2 il numero 0,625.
Cifre binarie
0,625 × 2 = 1,25 tolgo 1 1
0,25 × 2 = 0,5 0
0,5 × 2 = 1 tolgo 1 1
0 stop
12
Unitˆ didattica 1 - Sistemi di numerazione
Risulta quindi:
0,62510 = 0,1012
Controprova:
0,1012 = 2−1 + 0 + 2−3 = 1/2 + 1/8 = 5/8 = 0,62510
...........................................................................................................................................
SOMMA RISULTATO
0+0=0 0
0+1=1+0 1
1 + 1 = 10 0 con riporto di 1
Una volta definita la somma di due numeri di una sola cifra, • facile eseguire lÕaddizione di due nu-
meri binari, tenendo conto della regola del riporto.
Esempio..............................................................................................................................
Eseguire la somma: 1101002 + 111012
Base 2 Base 10
Riporti 1111 1
110100 + 52 +
11101 = 29 =
1010001 81
...........................................................................................................................................
Per addizionare due o pi• numeri binari si addizionano i primi, al risultato si aggiunge il terzo e cos“ via.
Cos“ facendo, si evitano le complicazioni dei riporti.
Sottrazione
La sottrazione pu˜ essere eseguita con la regola del prestito.
Esempio..............................................................................................................................
Eseguire la sottrazione 1101012 Ð 101102
Base 2 Base 10
Prestiti 1111
110100 Ð 53 Ð
10110 = 22 =
11111 31
...........................................................................................................................................
13
Sezione 1 - Premesse
Esempio..............................................................................................................................
Trasformare in base 10 il numero N16 = 2A.1116
2A.1116 = 2 × 161 + A × 160 + 1 × 16−1 + 1 × 16−2 =
= 2 × 16 + 10 × 1 + 1 × 1/16 + 1 × 1/256 =
= 32 + 10 + 0.0625 + 0.0039062
= 42.0664062
...........................................................................................................................................
14
Unitˆ didattica 1 - Sistemi di numerazione
Esempio..............................................................................................................................
Trasformare il numero 1970 in esadecimale.
1970 : 16 = 123 resto 2
123 : 16 = 7 resto 11 = B
7 : 16 = 0 resto 7
Cifre esadecimali: 7 B 2
Pertanto 1970 = 7B216.
...........................................................................................................................................
2. N • un numero frazionario.
In tal caso, procedendo come nella numerazione binaria, si converte la parte intera con il meto-
do appena visto e, per la parte non intera, si eseguono moltiplicazioni successive per 16: si toglie
di volta in volta la parte eccedente lÕunitˆ e la si trascrive come cifra della parte frazionaria.
Esempio..............................................................................................................................
Sia N = 18.6206
Cifre esadecimali
0.6206 × 16 = 9.9296 tolgo 9 9
0.9296 × 16 = 14.8736 tolgo 14 E (= 1410)
0.8736 × 16 = 13.9776 tolgo 13 D (= 1310)
0.9776 × 16 = 15.6416 tolgo 15 F (= 1510)
ecc.
Quindi 0.6206 = 0.9EDF... 16
Il calcolo • approssimato alla quarta cifra decimale.
...........................................................................................................................................
Addizione
LÕaddizione tra due numeri esadecimali viene eseguita tenendo conto della regola del riporto, cos“ co-
me in base 10 e in base 2. Quando si eseguono i calcoli in base 16, occorre ricordare che il riporto de-
ve essere calcolato quando si raggiunge o si supera 16, e non 10 (come ci suggerisce lÕabitudine).
Esempio..............................................................................................................................
Eseguire la somma 2A1C16 + 9E316
Base 16
Riporti 1
2A1C +
9E3 =
33FF
15
Sezione 1 - Premesse
Sottrazione
Come nel sistema binario, anche in quello esadecimale la sottrazione si esegue con la regola del prestito.
Esempio..............................................................................................................................
Eseguire la sottrazione 3C2E16 Ð 4A516
Base 16
Prestito 1
3C2E Ð
4A5 =
3789
si vede che a ogni cifra esadecimale corrisponde una quadrupla di cifre binarie. Ne consegue quanto ri-
portato nella regola che segue.
Per passare da un numero in forma binaria, intero o non intero, alla corrispondente
forma esadecimale si suddivide il numero binario in gruppi di quattro bit, dal punto
decimale verso sinistra e dal punto radice verso destra, completando se necessario
lÕultimo gruppo di sinistra e lÕultimo di destra con degli zeri. Quindi, si sostituisce a ogni
gruppo di 4 bit la corrispondente cifra esadecimale, secondo quanto indicato dalla
tabella riportata sopra.
16
Unitˆ didattica 1 - Sistemi di numerazione
Esempio..............................................................................................................................
N2 = 101101110.10111 = 0001 0110 1110 . 1011 1000
{
{
{
{
{
1 6 E B 8
Quindi
101101110.101112 = 16E.B816
...........................................................................................................................................
Dato un numero in forma esadecimale, per ottenere la sua forma binaria si sostitui-
sce ogni cifra della forma esadecimale, con il corrispondente gruppo di quattro ci-
fre binarie determinato sulla base della tabella riportata sopra.
Esempio..............................................................................................................................
N16 = A3516 = 1010 0011 0101 = 1010001101012
{
{
{
A 3 5
...........................................................................................................................................
I numeri esadecimali vengono usati in informatica per rappresentare i dati memorizzati in un elaboratore
e per correggere errori, ma raramente sono usati per lÕinput e lÕoutput di dati. I numeri esadecimali han-
no il vantaggio che con due cifre rappresentano otto bit e, come vedremo pi• avanti, possono esprimere
in forma sintetica tutti i caratteri comunemente usati per trasmettere le informazioni.
17
Sezione 1 - Premesse
Esercizi
Unità didattica 1
1 Per ognuno dei seguenti numeri, indicare il valore posizionale delle cifre 3, 7, 4.
A 37584
B 4735
C 78043
2 Scrivere i seguenti numeri decimali nella rappresentazione polinominale secondo le potenze di 10.
A 7039
B 180041
C 731.03
3 Quale numero indica la rappresentazione polinominale: 5 × 102 + 3 × 10? In quale sistema di numera-
zione?
qa Verificare che i tre numeri binari 1001, 111, 1101 abbiano per somma 11101.
qs Verificare nel sistema binario l’eguaglianza: ((11 + 10 + 101) × 11 – 101) /101 = 101.
18
Unità didattica 1 - Sistemi di numerazione
Esercizi
Unità didattica 1
w0 Verificare che il numero decimale 53 sia rappresentato da 110101 nella scrittura binaria e 35 nella scrit-
tura esadecimale.
19
Unitˆ didattica
2
Codifica
delle informazioni
CHE COSA IMPARERAI A FARE
Si possono citare, come esempi di codifica, la classificazione dei libri di una biblioteca, l’attribuzione
di un codice alla merce in vendita in un magazzino, l’immatricolazione di autovetture, ecc.
Quando le informazioni sono trattate in modo automatico, è necessario che la loro rappresentazione
simbolica sia intelligibile alla macchina. Per esempio, dovendo trattare informazioni numeriche con
macchine aventi dispositivi di calcolo adatti al sistema decimale, come è il caso delle macchine calco-
latrici e di quelle contabili, le informazioni devono essere presentate in forma numerica decimale e,
quindi, il codice usato è il codice numerico decimale.
Quando le informazioni devono essere trattate dai calcolatori elettronici, poiché i congegni che li
compongono possono esistere solo sotto due stati (passaggio o interruzione di corrente, oppure ma-
gnetizzazione positiva o negativa, tensione positiva o negativa), è evidente che le informazioni de-
vono essere rappresentate da un linguaggio binario il cui alfabeto è riportato di seguito.
A = {0, 1}
Sappiamo che le cifre binarie sono gli elementi fondamentali della tecnica dei calcolatori elettro-
nici e prendono il nome di bit. Ogni “carattere” e ogni “parola” devono essere ridotti a una suc-
cessione di bit, cioè di zero e uno, perché il calcolatore possa riconoscerli, elaborarli e fornire del-
le risposte.
I bit 0 e 1, presi due a due, possono dare quattro diverse configurazioni: 00; 01; 10; 11; quindi tan-
te quante sono le disposizioni con ripetizione di due oggetti presi due a due, ovvero 22 = 4.
Se i simboli binari sono presi quattro a quattro si ottengono 24 = 16 disposizioni con ripetizione e,
quindi, 16 configurazioni diverse.
21
Sezione 1 - Premesse
Per costruire un codice che rappresenti in forma binaria ogni carattere del linguaggio naturale, è
necessario stabilire una corrispondenza biunivoca tra ciascun carattere e una particolare configura-
zione binaria e, per convenzione, stabilire che tale configurazione rappresenti sempre quel carattere.
Si definisce codice del calcolatore l’insieme delle configurazioni binarie usate dalla
macchina per rappresentare i caratteri. L’insieme dei caratteri che possono essere
codificati è chiamato repertorio o set di caratteri del calcolatore.
I codici adottati per la rappresentazione in forma binaria delle informazioni numeriche e alfanume-
riche possono variare in funzione delle tecniche costruttive del calcolatore.
Quindi ogni tipo di calcolatore ha un proprio codice, detto codice macchina.
Nei paragrafi che seguono verranno illustrati brevemente alcuni di tali codici.
È bene, tuttavia, precisare che la codifica è un problema interno del calcolatore, che non riguarda di-
rettamente l’operatore umano.
I caratteri, sia numerici sia letterali, sono immessi ed estratti nella loro forma consueta.
Codice ASCII
Ogni carattere viene rappresentato con otto bit e, quindi, questo codice può rappresentare 28 = 256
caratteri.
Per rappresentare cifre, lettere e altri caratteri, come i segni di interpunzione, viene usato un codice
a 7 posizioni: il codice ASCII (American Standard Code for Information Interchange).
Numero Codice Binario Esadecimale Numero Codice Binario Esadecimale
d’ordine ASCII d’ordine ASCII
0 nul 0000000 00 64 @ 1000000 40
1 soh 0000001 01 65 A 1000001 41
2 stx 0000010 02 66 B 1000010 42
3 etx 0000011 03 67 C 1000011 43
4 eot 0000100 04 68 D 1000100 44
5 enq 0000101 05 69 E 1000101 45
6 ack 0000110 06 70 F 1000110 46
7 bel 0000111 07 71 G 1000111 47
8 bs 0001000 08 72 H 1001000 48
9 ht 0001001 09 73 I 1001001 49
10 lf 0001010 0A 74 J 1001010 4A
22
Unitˆ didattica 2 - Codifica delle informazioni
23
Sezione 1 - Premesse
I caratteri stampabili, quelli compresi dal 32 al 126, rappresentano tutti i simboli di cui un computer
pu˜ aver bisogno per le proprie elaborazioni: lettere, segni dÕinterpunzione, simboli matematici, ca-
ratteri numerici e cos“ via.
I caratteri di controllo (quelli da 0 a 31 e il 127) sono caratteri non stampabili, che vengono impie-
gati per segnalare condizioni particolari, per esempio eot per individuare la fine di un testo o ack
per indicare il riconoscimento di un segnale durante una trasmissione dati.
I numeri che figurano nella tabella (i codici dal 48 al 57) non vanno confusi con i valori numerici pro-
priamente detti, che abbiamo studiato in precedenza: in questo caso si tratta semplicemente dei sim-
boli che servono a rappresentare le cifre.
La codifica ASCII • realizzata con 7 bit, ma ciascun carattere occupa, in ogni caso, un byte (8 bit).
Per allargare lÕinsieme dei caratteri rappresentabili, includendo anche le vocali accentate e i segni
diacritici tipici delle lingue latine, ai 128 simboli del codice ASCII a 7 bit sono stati aggiunti altri
128 caratteri. Il codice ASCII a 8 bit che permette di rappresentare 256 caratteri prende il nome di
Codice ASCII esteso.
Codice UNICODE
A differenza delle lingue anglosassoni, come inglese e tedesco, e neolatine, come italiano, francese e
spagnolo, esistono molti linguaggi che non usano lÕalfabeto latino: greco e cirillico ne sono un tipi-
co esempio. Per la loro rappresentazione si ricorre a speciali codici a 8 bit, che descrivono insiemi spe-
cifici di caratteri. Il computer di un utente russo, per esempio, deve poter rappresentare un insieme
di caratteri ben diverso da quello di un utente italiano. Oltre ai vari tipi di codici a 8 bit esistono an-
che codici a 16 bit, che vengono utilizzati per linguaggi ancora pi• complessi come quelli dei paesi
asiatici. Vista la varietˆ delle codifiche, si • cercato uno standard di rappresentazione che comprenda
universalmente tutti i caratteri possibili: nasce con questo scopo il codice UNICODE, che con i suoi
2 byte di rappresentazione consente di descrivere ben 65.536 caratteri diversi.
24
Unità didattica 2 - Codifica delle informazioni
La lunghezza della parola dipende dal codice interno del calcolatore e può essere di 8, 16, 32, 64 bit.
Naturalmente, un numero intero non può essere rappresentato da un numero di bit maggiore di
quello della parola del calcolatore, pertanto l’insieme degli interi memorizzabili all’interno della
macchina è un sottoinsieme dell’insieme degli interi. Se n è il numero di bit contenuti in una parola
di un calcolatore, tenuto conto che il primo bit a sinistra rappresenta il segno del numero, i numeri
interi rappresentabili sono compresi tra:
− (2n−1 − 1) e 2n−1 − 1
Per esempio, con parole di 8 bit si possono rappresentare gli interi compresi tra:
−27 − 1 e 27 − 1
Il fatto di operare su un insieme limitato degli interi comporta notevoli conseguenze nell’aritmetica
dei calcolatori. Infatti è indispensabile che i risultati delle operazioni, in fase di esecuzione, appar-
tengano all’intervallo −2n − 1 e 2n − 1, altrimenti si determina una situazione di traboccamento o, in
inglese, di overflow. In questo caso la macchina, in generale, opera un troncamento sul risultato op-
pure interrompe l’esecuzione, segnalando con un messaggio l’errore.
Per esempio, nel caso di n = 8, la semplice espressione:
(90 + 40) + (−20)
risulta uguale a:
130 + (−20)
e, poiché 130 > 27, si dice che “si va in overflow”.
Anche se i calcolatori moderni permettono di operare su un sottoinsieme molto ampio degli interi,
per cui l’overflow si presenta raramente, è buona norma di sicurezza prevedere questi casi di traboc-
camento, soprattutto per quelle operazioni come la moltiplicazione e la potenza, che portano con
più velocità di altre operazioni a valori molto elevati o molto piccoli. Naturalmente, i limiti entro cui
si opera dipendono, come già detto, dal tipo di calcolatore usato.
Rappresentazione in complemento a 2
Viene ora presentato il metodo universalmente riconosciuto come il più valido per la rappresen-
tazione dei numeri interi relativi. La sua conoscenza permette di spiegare alcuni casi di errore di
calcolo imprevisti, dovuti alla mancata segnalazione degli eventi di overflow da parte dell’elabo-
ratore.
25
Sezione 1 - Premesse
Esempio..............................................................................................................................
Per la rappresentazione di x = –12 con parole di 8 bit, il modulo da scrivere in binario è:
27 − 12 = 128 − 12 = 116 = 1110 1002
quindi, la rappresentazione su 8 bit di x = −12 è:
1 1 1 1 0 1 0 0
⇓
bit del segno
...........................................................................................................................................
In base a quanto detto, usando parole di n bit sono rappresentabili in complemento a due tutti i nu-
meri compresi nell’intervallo:
−2n−1 ; 2n−1 − 1
Se nella rappresentazione di un numero negativo cambiamo gli 1 in 0 e gli 0 in 1, si ottiene il valore as-
soluto del numero diminuito di 1.
Esempio..............................................................................................................................
Dall’esempio precedente si ha che la rappresentazione di –12 è 11110100; cambiando i
bit, come detto, si ha 00001011, che rappresenta il numero decimale 11, che è il valore
assoluto di –12 diminuito di 1.
...........................................................................................................................................
Tenendo conto di questa considerazione, è possibile ricorrere alla procedura seguente per calcolare
in modo rapido la rappresentazione in complemento a 2 di un numero negativo.
Esempio..............................................................................................................................
Rappresentare in complemento a 2 il numero x = –18, su otto bit.
+18 è uguale a 0 0 0 1 0 0 1 0
Scambio gli 0 con 1 e gli 1 con 0 1 1 1 0 1 1 0 1
Aggiungo 1 + 1
–18 è uguale a 1 1 1 0 1 1 1 0
⇑
bit del segno 27 – 18 = 110
...........................................................................................................................................
26
Unitˆ didattica 2 - Codifica delle informazioni
Quando si utilizza il criterio del complemento a 2 per rappresentare i numeri negativi, • indispensabile
specificare con chiarezza il numero di bit disponibile per le operazioni aritmetiche di somma o sottrazio-
ne. AllÕinterno di un elaboratore, tale numero • definito dalla lunghezza massima della parola (ovvero del
numero di byte) con cui lÕelaboratore opera. Il numero di bit utilizzati permette di conoscere la posizione
del segno e di controllare eventuali overflow.
Esempio..............................................................................................................................
Supponendo di operare con parole di un byte (cio• con 8 bit), eseguire le seguenti operazioni:
a) 15 − 12;
b) −15 − 12;
c) 100 + 30.
a)
a) 1 5 Ð 1 2
Quindi
Base 10 Base 12
+1 5 + 0 0 0 0 1 1 1 1 +
Ð1 2 = 1 1 1 1 0 1 0 0 =
+3 1 0 0 0 0 0 0 1 1 =+3
⇑
Riporto da troncare
b)
b) Ð1 5 Ð 1 2
Quindi
Base 10 Base 2
Ð1 5 + 1 1 1 1 0 0 0 1 +
Ð1 2 = 1 1 1 1 0 1 0 0 =
Ð2 7 1 1 1 1 0 0 1 0 1 = complemento a 2 di 27
⇑
Riporto da troncare
27
Sezione 1 - Premesse
Verifica
Ð 2 7 = 1 1 1 0 0 1 0 12 complemento a 2
⇑
1 1 1 0 0 1 0 1 scambio 0 con 1
0 0 0 1 1 0 1 0 sommo 1
0 0 0 1 1 0 1 1
+ 2 7 = 0 0 0 1 1 0 1 12 ⇑
c)
c) 1 0 0 + 30
1 2 6 = 0 1 1 1 1 1 1 02 complemento a 2
⇑
0 1 1 1 1 1 1 0 scambio 0 con 1
1 0 0 0 0 0 0 1 sommo 1
1 0 0 0 0 0 1 0
Ð 1 2 6 = 1 0 0 0 0 0 1 02 ⇑
...........................................................................................................................................
28
Unità didattica 2 - Codifica delle informazioni
la cui aritmetica ammetta solo quattro cifre decimali il numero reale EF 2 sarà rappresentato dal deci-
male finito 1.4142. Ogni decimale finito rappresenta inoltre un sottoinsieme di numeri reali: per
esempio, 1.4142 è la rappresentazione di tutti i reali, razionali o irrazionali, le cui prime quattro ci-
fre frazionarie sono 4, 1, 4, 2 (1.41427, 1.4142333..., 1.41424141..., sono esempi di elementi del-
l’insieme rappresentato dal decimale finito 1.4142). Da quanto esposto si può facilmente compren-
dere che sorgono notevoli problemi, sia di rappresentazione dei numeri reali sia di approssimazione,
quando si tratta dell’aritmetica dei calcolatori.
In molti problemi, soprattutto di carattere scientifico, vengono spesso trattati numeri molto piccoli
o molto grandi, con numerosi zeri, per cui è necessario rappresentare i numeri in modo da evitare la
scrittura ripetuta di molti zeri. In questi casi viene adottato un particolare metodo, detto della vir-
gola mobile (floating point, in inglese), che consente di ridurre notevolmente l’impiego di posizioni
di memoria fisiche del calcolatore.
Con il metodo della virgola mobile i numeri vengono scritti in forma esponenziale, in
modo che la parte intera sia sempre zero e la prima cifra frazionaria sia diversa da ze-
ro: ciò si ottiene moltiplicando o dividendo il numero per un’opportuna potenza di
dieci.
I numeri +32.415; −2130000; +0.0000003715 vengono rappresentati in virgola mobile come se-
gue:
2
+3 2 . 4 1 5 = 0 . 3 2 4 1 5 * 1 0
7
–2 1 3 0 0 0 0 = –0 . 2 1 3 * 1 0
–6
+0 . 0 0 0 0 0 0 3 7 1 5 = +0 . 3 7 1 5 * 1 0
Per esempio, nel numero +32.415 = +0.32415 * 102 il valore 32415 è la mantissa, mentre l’espo-
nente 2 di 10 è la caratteristica.
La scrittura in virgola mobile di un numero viene anche detta forma normalizzata del numero.
In memoria centrale il numero, scritto in forma normalizzata, viene rappresentato secondo conven-
zioni particolari, che possono variare da calcolatore a calcolatore, ma che tendono tutte a rendere mi-
nimo il numero di posizioni che il numero deve occupare in memoria.
29
Sezione 1 - Premesse
Da questi esempi si può comprendere come questo tipo di rappresentazione dei numeri in memoria
riduca notevolmente il numero di posizioni occupate, soprattutto quando i numeri contengono
molti zeri.
Esistono rappresentazioni in virgola mobile in cui sia la mantissa sia la caratteristica sono espresse in
esadecimale e la caratteristica viene aumentata di 64: in tal modo, l’esponente convenzionale varia da
0 a 127 e quello effettivo da –64 a +63.
Da quanto esposto si può concludere che l’aritmetica dei dati reali è un’aritmetica finita e discreta e
l’insieme su cui opera è un piccolissimo sottoinsieme dei numeri reali.
In questa esposizione ci siamo avvalsi della scrittura decimale ma, naturalmente, all’interno del cal-
colatore i numeri vengono espressi in forma binaria secondo le potenze di 2.
Una rappresentazione che utilizza quattro byte potrebbe essere così strutturata:
Poiché il massimo numero rappresentabile con 7 bit è 127, l’esponente convenzionale varia da 0 a
127 mentre l’esponente effettivo, che si ottiene togliendo 64, varia da –64 a 63.
Supposto che l’esponente convenzionale sia 10001002, che in decimale corrisponde al numero 68,
l’esponente effettivo sarà 68 – 64 = 4.
30
Unità didattica 2 - Codifica delle informazioni
Esercizi
Unità didattica 2
3 Quanti bit sono necessari per codificare i caratteri con cui vengono rappresentate le informazioni?
4 Nella tabella del codice ASCII un bit resta sempre uguale a 0. Quale? Perché?
9 Dopo aver trasformato gli operandi in base 2, eseguire l’operazione 33 + 71 utilizzando il criterio
del complemento a 2 avendo a disposizione 8 bit.
q0 Dopo aver trasformato gli operandi in base 2, eseguire l’operazione 24 + 62 utilizzando il criterio
del complemento a 2 avendo a disposizione 8 bit.
qa Dopo aver trasformato gli operandi in base 2, eseguire l’operazione 71 – 33 utilizzando il criterio
del complemento a 2 avendo a disposizione 8 bit.
qs Dopo aver trasformato gli operandi in base 2, eseguire l’operazione 62 – 24 utilizzando il criterio
del complemento a 2 avendo a disposizione 8 bit.
qd Dopo aver trasformato gli operandi in base 2, eseguire l’operazione 33 – 71 utilizzando il criterio
del complemento a 2 avendo a disposizione 8 bit.
qf Dopo aver trasformato gli operandi in base 2, eseguire l’operazione 33 – 34 utilizzando il criterio
del complemento a 2 avendo a disposizione 8 bit.
qg Dopo aver trasformato gli operandi in base 2, eseguire l’operazione 133 – 71 utilizzando il criterio
del complemento a 2 avendo a disposizione 8 bit. Dire perché il risultato è da ritenersi errato.
qh Dopo aver trasformato gli operandi in base 2, eseguire l’operazione 64 + 64 utilizzando il criterio
del complemento a 2 avendo a disposizione 8 bit. Dire perché il risultato è da ritenersi errato.
31
Sezione 1 - Premesse
Esercizi
Unità didattica 2
qj Scrivere in base 2 tutti i numeri che possono essere rappresentati con 4 bit quando si utilizza il metodo
del complemento a 2.
32
Sezione 2
Primi elementi
di programmazione
†
Obiettivi ◊ Acquisire familiaritˆ con la struttura di un programma
C++
◊ Apprendere i primi elementi del linguaggio
◊ Scrivere, compilare ed eseguire brevi programmi
in C++
Codice
Ciao.cpp
1 #include <iostream>
2 using namespace std;
3 //inizio
4 int main ()
5 {
6 //scrivi il messaggio di saluto
7 cout << “Ciao, mondo” << endl;
8 //arresta l’esecuzione del programma
9 system(pause);
10 //termina il programma
11 return 0;
12 }
...........................................................................................................................................
step by step 쩚 쩚 쩚 쩚 쩚 쩚 쩚 쩚 쩚 쩚 쩚 쩚 쩚 쩚 쩚 쩚 쩚 쩚 쩚 쩚 쩚 쩚 쩚 쩚 쩚 쩚 쩚
È importante ricordare che si tratta di un file che contiene codice C++, che deve avere l’estensione .cpp.
35
Sezione 2 - Primi elementi di programmazione
Per programmi scritti in C++ • sufficiente un editor di base come Blocco Note di Windows. LÕedi-
tor di testi permette di scrivere un programma, cio• un insieme finito di istruzioni composte da un
limitato numero di parole chiave relative alla grammatica di un determinato linguaggio di pro-
grammazione.
Il file che contiene il testo del programma viene chiamato file sorgente, o program-
ma sorgente, ed • un semplice file di testo che viene letto dal compilatore; questÕulti-
mo provvede alla codifica delle istruzioni scritte dal programmatore in istruzioni di-
rettamente eseguibili dal processore.
I file sorgente, perlomeno quelli di programmi di una certa dimensione, sono in genere pi• di uno e
tra essi rivestono particolare importanza i file chiamati librerie (anche detti file dÕintestazione), che
tipicamente contengono insiemi di funzioni, procedure e classi utilizzati dal programma principale.
Il meccanismo di inclusione dei file dÕintestazione permette di realizzare programmi indipendenti
dallÕhardware e dal software di sistema, cio• dal compilatore e dal sistema operativo in uso sul com-
puter sul quale il programma viene eseguito: infatti, basterˆ creare librerie di sottoprogrammi conte-
nenti tutte le funzioni dipendenti dal tipo di elaboratore, che saranno richiamate nel codice sorgen-
te del programma tramite il loro file dÕintestazione.
AffinchŽ il programma sia completo deve essere collegato (mediante un apposito software, detto
linker) con altri codici oggetto. I sottoprogrammi utilizzati dal linker per effettuare il collegamento
sono moduli che possono essere forniti dal sistema operativo, scritti dal programmatore e/o forniti
insieme al compilatore. La compilazione e il linking di un codice sorgente producono il file esegui-
bile (di estensione .exe), il quale viene memorizzato su unÕapposita area di memoria di massa atta a
contenere il codice eseguibile.
Un file eseguibile racchiude al suo interno una serie dÕistruzioni in formato binario
(quindi interpretabili dal processore) che devono essere eseguite secondo un ordine
preciso.
Il file eseguibile • strettamente dipendente dal sistema operativo della macchina su cui viene esegui-
to. Per esempio, non • possibile trasferire un file eseguibile da un sistema operativo Unix su una mac-
china Windows: lÕinsieme delle informazioni contenute nel file eseguibile destinato al sistema opera-
tivo Unix, infatti, contiene istruzioni che Windows non • in grado di eseguire.
Il passaggio da codice sorgente a file eseguibile • detto compilazione e viene eseguito da un pro-
gramma detto compilatore.
In ambiente C++ il processo di compilazione • in realtˆ diviso in due parti ben distinte:
Il parsing del codice sorgente • la prima attivitˆ che viene svolta durante il processo di compilazione.
36
Unitˆ didattica 3 - Introduzione a C++
Qualora siano presenti, gli errori di scrittura nel programma sorgente vengono segnalati, sotto for-
ma di errori di compilazione, al programmatore che dovrˆ correggere il suo programma riprenden-
done la fase di editing.
Una volta che il parser ha esaminato lÕintero file e ne ha verificato la correttezza sintattica e gram-
maticale • la volta del compilatore vero e proprio, che deve trasformare le istruzioni in linguaggio
di programmazione contenute nel file sorgente in una sequenza di istruzioni in linguaggio mac-
china.
Ogni linguaggio di programmazione provvede a individuare i propri file sorgente, obbligando il pro-
grammatore a salvarli con una determinata estensione: per esempio nel linguaggio C i file sorgente
hanno estensione .c, in C++ hanno .cpp, in Java .java.
Un file eseguibile, frutto della compilazione di un file sorgente, ha estensione .exe e contiene istru-
zioni direttamente eseguibili dalla CPU. La figura che segue riporta lo schema logico riassuntivo dei
vari passaggi che portano dal programma sorgente al programma eseguibile.
Programmatore
Editor Parser
File sorgente Librerie
S“
Errori
No
Compilatore
Moduli
File eseguibile
LÕesecuzione del programma finale avviene sotto il controllo del sistema operativo, seguendo i pas-
saggi principali indicati di seguito.
1. Trasferimento del contenuto del file eseguibile dalla memoria di massa nella RAM.
2. Inizio dellÕesecuzione del programma, mediante il richiamo della funzione main() da parte
del sistema operativo.
3. Svolgimento dellÕelaborazione.
4. Termine dellÕelaborazione.
LÕinizio dellÕesecuzione di un programma finale scritto in C++ coincide con la chiamata alla funzio-
ne main() da parte del sistema operativo.
Lo schema della figura applicato al primo programma ci mostra i diversi passaggi necessari per arri-
vare dalla scrittura del file sorgente Ciao.cpp fino allÕesecuzione del programma eseguibile
Ciao.exe.
Dopo che il programma • stato compilato, • possibile mandarlo in esecuzione.
37
Sezione 2 - Primi elementi di programmazione
Prova di esecuzione
Come si nota, il programma esegue semplicemente la visualizzazione del messaggio ÒCiao, mondo!Ó.
Ciao.cpp
1 #include <iostream>
2 using namespace std;
3 //inizio
4 int main ()
5 {
6 //scrivi il messaggio di saluto
7 cout << ÒCiao, mondoÓ << endl;
8 //arresta lÕesecuzione del programma
9 system(pause);
10 //termina il programma
11 return 0;
12 }
La riga 1 indica al compilatore di leggere la libreria di funzioni iostream e di inserirla nel codice. Ta-
le file contiene la definizione delle funzioni e degli operatori per la gestione dei flussi di immissione e di
emissione. La presenza di questÕistruzione permette lÕesecuzione dellÕistruzione cout << "Ciao,
mondo!" << endl; per questo • necessario includere un riferimento a questa libreria in tutti i pro-
grammi che leggono dati dalla testiera o scrivono dati a video.
La riga 2 indica al compilatore che tutti i nomi utilizzati nel programma appartengono al namespace
standard del C++. Nei programmi di grandi dimensioni • alquanto comune che programmatori diver-
si utilizzino gli stessi nomi per denotare elementi diversi: si possono evitare conflitti di nomi utilizzan-
do namespace separati, tuttavia per i semplici programmi che verranno scritti in questo libro tale misu-
ra non • asolutamente necessaria.
Le righe 3, 6, 8 e 10 non sono istruzioni, bens“ commenti.
I commenti sono utilizzati in genere come una descrizione delle funzionalitˆ di una
istruzione, di un intero blocco di codice o come informazioni sullÕautore del pro-
gramma. In altre parole: tutto ci˜ che serve per informazioni e/o descrizioni, va inse-
rito in un commento.
Il compilatore, allÕatto della generazione del codice eseguibile, ignora i commenti
lavorando solo sul codice del programma.
38
Unità didattica 3 - Introduzione a C++
: commenti a riga singola, che iniziano con la combinazione di caratteri // e si estendono solo
fino alla fine della riga;
: commenti delimitati, che iniziano con la combinazione di caratteri /*, terminano con la com-
binazione */ e possono estendersi su più righe, come nell’esempio riportato di seguito.
/*
Questo programma scrive un
messaggio di benvenuto
*/
Il costrutto individuato dalle righe 4, 5, 11, 12 del codice definisce una funzione denominata main.
Una funzione rappresenta una raccolta di istruzioni di programmazione che consente di portare a
termine una determinata operazione.
Ogni programma C++ deve disporre di una funzione main. La maggior parte dei programmi C++
contiene altre funzioni oltre a main, il cui ruolo spiegheremo più avanti. Le istruzioni del corpo del-
la funzione main, vale a dire quelle all’interno delle parentesi graffe {}, vengono eseguite una per
volta, in sequenza.
Tutti i programmi C++ iniziano l’esecuzione con questo costrutto: nel nostro esempio le istruzioni
che vanno da riga 6 a riga 12 sono delimitate dalle parentesi di riga 5 e riga 13, che definiscono un
blocco di codice “appartenente” alla funzione main, a sua volta “appartenente” al programma
Ciao.cpp.
Come vedremo, esistono molti costrutti che utilizzano le parentesi graffe per raggruppare un insie-
me di istruzioni.
La riga 7 contiene l’istruzione che realizza concretamente quanto richiesto dal programma: infatti, il
comando cout richiede che venga scritto a video quanto indicato dopo di esso.
La sequenza di caratteri racchiusa da virgolette “Ciao, mondo!” viene denominata stringa. È
necessario racchiuderne il contenuto all’interno delle virgolette, per fare in modo che il compi-
latore ne comprenda il significato letterale. In questo breve programma non è possibile alcuna
confusione, ma si supponga di voler visualizzare il termine “main” sullo schermo: indicando il ter-
mine tra virgolette, vale a dire specificando la stringa “main” dopo il comando cout, il compi-
latore comprende che si tratta della sequenza di caratteri “main”, non della funzione denominata
main. In C++ la semplice regola di racchiudere tutte le stringhe di testo tra virgolette è sufficien-
te per fare in modo che il compilatore le interpreti come semplice testo e non come istruzioni del
programma.
L’instruzione si conclude con la parola chiave endl (che sta per “end line”), che comunica al siste-
ma operativo che la stringa è terminata e che il cursore dello schermo deve andare a capo dopo aver-
la scritta.
La riga 9 sospende l’esecuzione del programma e comunica all’utente il messaggio
Solo dopo che l’utente ha effettuato quanto indicato, l’esecuzione del programma procede alla riga
successiva. Il comando di arresto system (“pause”); non è indispensabile e può essere omesso
senza alterare la logica del programma, ma è molto utile perché permette all’utente di controllare
quanto è stato realizzato dal programma e quali sono i risultati da esso prodotti.
La riga 12, infine, chiude il codice della funzione main e restituisce il controllo al sistema ope-
rativo.
39
Sezione 2 - Primi elementi di programmazione
Si ricordi sempre che ogni istruzione C++ valida termina con un “;” (il carattere “punto e virgola”), altrimenti
il compilatore genera un errore di sintassi.
Una variabile è una zona (locazione) di memoria che memorizza un determinato ti-
po di dato, identificato e accessibile tramite un nome mnemonico.
Attribuire un nome a una locazione di memoria anziché avere a che fare con un incomprensibile in-
dirizzo formato da lunghe sequenze esadecimali solleva il programmatore da un’enorme fatica: si im-
magini un programma che ha al proprio interno centinaia di variabili codificate con il loro indirizzo
di memoria! Probabilmente tutti i linguaggi di alto livello come il C++ non avrebbero avuto motivo
di esistere: si sarebbe continuato a programmare in codice macchina e si sarebbe fatta una fatica enor-
me a progettare le applicazioni visuali che oggi siamo abituati a utilizzare.
Per utilizzare una variabile, è necessario definirla (dichiarazione) all’interno del codi-
ce, nel seguente modo:
nometipo nomevariabile;
dove:
: nometipo specifica il tipo di dato che si intende memorizzare nella variabile (ve-
dremo più avanti i vari tipi di dato);
: nomevariabile identifica la variabile tramite un nome.
Codice
Numero.cpp
1 #include <iostream>
2 using namespace std;
3 //inizio
4 int main ()
5 {
6 //dichiara la variabile di tipo intero
40
Unitˆ didattica 3 - Introduzione a C++
7 int numero;
8
9 //assegna il valore
10 numero=122;
11
12 //scrivi il valore di numero
13 cout << numero << endl;
14
15 //salta due righe
16 cout << endl << endl;
17 //arresta l'esecuzione del programma
18 system ("pause");
19 //termina il programma
20 return 0 ;
21 }
Prova di esecuzione
Si noti che dopo il costrutto cout<< non cÕ• pi• una stringa di caratteri, ma una variabile: questo signi-
fica che lÕistruzione riconosce automaticamente che deve essere visualizzato il valore contenuto nella va-
riabile numero. Le operazioni di dichiarazione e di assegnazione di una variabile sono molto semplici, ma
bisogna considerare che il valore da assegnare a una variabile pu˜ essere anche il risultato dellÕesecu-
zione di unÕespressione matematica. LÕimportante • dunque che il valore, comunque sia determinato, ri-
spetti il tipo di dato associato alla variabile.
...........................................................................................................................................
Nomi di variabili
Negli esempi visti fino a questo punto sono state utilizzate poche variabili, quindi non sono sorti pro-
blemi di chiarezza nellÕinterpretazione del codice. é utile, tuttavia, comprendere lÕimportanza di at-
tribuire a una variabile il nome pi• adatto.
Quando, infatti, si scriveranno programmi pi• complessi, con un numero considerevolmente pi• al-
to di variabili, • consigliabile che queste abbiano un nome il pi• possibile autodescrittivo. Per esem-
pio, se si intende scrivere un programma che calcola lÕarea di un triangolo • opportuno attribuire al-
la variabile che contiene il valore della base il nome base e a quella che contiene lÕaltezza il nome
altezza. Ci˜ si traduce in un immediato riconoscimento delle variabili e del loro utilizzo allÕinter-
no del programma.
41
Sezione 2 - Primi elementi di programmazione
In C++ esistono molti tipi di dato, ognuno dei quali necessita di una quantitˆ di memoria idonea a
contenerlo. Per dichiarare una variabile bisogna sapere di che tipo •.
NellÕultimo esempio esaminato • stata dichiarata una variabile di tipo numerico, in particolare di ti-
po intero, denominata int.
42
Unitˆ didattica 3 - Introduzione a C++
Il C++ dispone di tre tipi di dato intero, articolati nelle categorie riportate nella tabel-
la che segue.
TIPO C++ MIN MAX
short Ð32768 32767
int Ð2.147.483.648 2.147.483.647
long Ð9.223.372.036.854.775.808 9.223.372.036.854.775.807
Tipo int
Un tipo di dato int occupa 4 byte di memoria, cio• 32 bit. Esso pu˜ contenere un numero intero
con segno compreso tra:
Ð2.147.483.648 e 2.147.483.647
corrispondenti alle espressioni Ð231 e 231 Ð 1.
Codice
Intero.cpp
1 #include <iostream>
2 using namespace std;
3 // inizio
4 int main ()
5 {
6 //dichiara la variabile e assegnale un valore
7 int num = 1234567890;
8
9 //scrivi num
10 cout << "intero = " << num;
11
12 //salta due righe
13 cout << endl << endl;
14 //arresta l'esecuzione del programma
15 system ("pause");
16 //termina il programma
17 return 0 ;
18 }
Prova di esecuzione
43
Sezione 2 - Primi elementi di programmazione
Tipo short
Il tipo di dato intero, come abbiamo visto, occupa 4 byte di memoria, ma spesso • necessario elabo-
rare numeri molto piccoli, che sicuramente non eccedono i valori compresi tra
Ð32.768 e 32.767
cio• Ð215 e 215 Ð1.
In questo potremmo utilizzare agevolmente il tipo di dato short.
Il tipo short occupa 2 byte, cio• 16 bit di memoria, e consente un notevole risparmio di me-
moria.
Tipo long
Se in un programma si devono manipolare numeri interi molto grandi, cÕ• bisogno di un tipo di da-
to capace di contenere tali numeri.
A tale scopo alcuni compilatori C++ (ma non tutti) mettono a disposizione il tipo long che utilizza
ben 8 byte (cio• 64 bit) e pu˜ memorizzare un numero compreso tra:
Ð9.223.372.036.854.775.808 e 9.223.372.036.854.775.807
cio• tra Ð263 e 263 Ð1.
Quando si progetta un programma si deve tener presente il giusto utilizzo del tipo di dato, onde evi-
tare inutili sprechi di memoria; • quindi consigliabile pensare bene a quale tipo di dato si adatti me-
glio allÕelaborazione e al trattamento dei dati, prima di procedere con le fasi successive dello svilup-
po del programma.
I caratteri UNICODE sono valori a 16 bit che rappresentano gran parte delle lingue
scritte conosciute.
44
Unitˆ didattica 3 - Introduzione a C++
AllÕinterno di un programma, per poter assegnare un carattere a una variabile • necessario racchiu-
derlo tra apici singoli. Di seguito riportiamo alcune righe di esempio.
I caratteri possono essere espressi anche sotto forma di codici strettamente UNICODE. Quelli di uso
pi• diffuso, ossia quelli occidentali, rientrano nellÕintervallo di valori che va da 0 a 255 in notazione
decimale, ma il computer li interpreta sempre secondo il sistema esadecimale (quindi non utilizza la
base 10, ma la base 16). Si tenga presente che esiste sempre la possibilitˆ di visualizzare il carattere
corrispondente a un numero.
Codice
Carattere.cpp
1 #include <iostream>
2 using namespace std;
3 //inizio
4 int main ()
5 {
6 /*dichiara una variabile char e assegnale un numero intero
7 compreso tra 0 e 255 */
8 char car=(char)125;
9
10 //scrivi il carattere corrispondente al numero 125
11 cout << car;
12
13 //salta due righe
14 cout << endl << endl;
15 //arresta l'esecuzione del programma
16 system ("pause");
17 //termina il programma
18 return 0 ;
19 }
Prova di esecuzione
45
Sezione 2 - Primi elementi di programmazione
spondente. Per il momento si segnala che questo tipo di conversione viene chiamato casting espli-
cito, in gergo informatico: pi• avanti se ne parlerˆ ancora.
...........................................................................................................................................
double • il primo tipo di dato che il C++ utilizza per memorizzare i numeri con la virgola.
Ogni valore di questo tipo occupa 8 byte di memoria ed • compreso tra i seguenti in-
tervalli di valori:
Ð1,7 ××10308 e Ð5,0 ××10Ð324
La precisione di un valore di tipo double • di circa 15 cifre. Per esempio si pu˜ scrivere, senza incor-
rere in errori, lÕistruzione di assegnazione riportata di seguito.
double x=3.145926587412536;
Codice
Double.cpp
1 #include <iostream>
2 using namespace std;
3 //inizio
4 int main ()
5 {
6 //dichiara la variabile di tipo double
7 double numero;
8
9 //assegna un valore
10 numero=3.145926587412;
11
12 //scrivi il valore
13 cout << numero << endl;
14
15 //salta due righe
16 cout << endl << endl;
17 //arresta l'esecuzione del programma
18 system ("pause");
19 //termina il programma
20 return 0 ;
21 }
46
Unitˆ didattica 3 - Introduzione a C++
Prova di esecuzione
Come si pu˜ vedere, a video non vengono riportate tutte le cifre decimali: per maggiori dettagli
su come gestire la precisione dellÕoutput si rimanda al paragrafo sulla formattazione dellÕUnitˆ di-
dattica 4.
...........................................................................................................................................
#include <iostream>
using namespace std;
int main()
{
double original_price = 3E14;
double discounted_price = original_price - 0.05;
double discount = original_price - discounted_price;
/* la differenza dovrebbe valere 0.05 */
cout Ç discount Ç "\n"; /* viene visualizzato 0.0625 ! */
}
Nella maggior parte dei programmi, come pure in quelli riportati in questo libro, la precisione, nel
trattamento dei numeri di tipo double, non costituisce in ogni caso un problema.
C++ dispone di un altro tipo di numeri a virgola mobile, denominato float, che prevede una pre-
cisione molto pi• limitata, ristretta a circa 7 cifre decimali.
Questo tipo, di norma, non dovrebbe essere utilizzato nei programmi, dato che la sua limitata pre-
cisione pu˜ costituire un problema in molti casi e tutte le funzioni matematiche restituiscono da-
ti di tipo double. Se si memorizzano i risultati di unÕoperazione matematica in una variabile di ti-
po float il compilatore segnala la possibile perdita di informazioni, quindi per evitare questi
messaggi di avvertimento e per mantenere la precisione dei risultati • consigliabile evitare di uti-
lizzare variabili di questo tipo.
47
Sezione 2 - Primi elementi di programmazione
3.8 Casting
Può capitare di memorizzare un valore di un certo tipo in una variabile di tipo differente. Ogni vol-
ta che si corre il rischio di perdere delle informazioni il compilatore lo segnala con un messaggio di
avvertimento. Se, per esempio, si memorizza un valore double in una variabile int, si possono per-
dere informazioni per due motivi:
L’istruzione int p = 1.0E100; non è corretta, dato che 10100 è un valore superiore all’intero più
grande memorizzabile in una variabile di tipo intero.
A volte, comunque, si rende necessario convenire un valore a virgola mobile in un valore intero. Se
è possibile perdere la parte frazionaria e se il numero a virgola mobile non supera il massimo valore
intero possibile si può evitare il messaggio di avvertimento ricorrendo a un’operazione di casting,
che consiste nella conversione da un tipo (per esempio, double) in un altro (per esempio, int).
L’istruzione riportata di seguito è un esempio di casting in C++.
Una variabile di tipo bool occupa 1 byte di memoria e la sua sintassi è molto sempli-
ce; per esempio, con:
bool flag=true;
si è dichiarata una variabile di tipo bool con l’assegnazione del valore true (vero).
Questa variabile si potrebbe utilizzare come segnalatore (flag) dell’esito di una ela-
borazione testandone il contenuto, cioè true oppure false.
Codice
Boolean.cpp
1 #include <iostream>
2 using namespace std;
3 //INIZIO
4 int main ()
5 {
6 //dichiara e inizializza a true la variabile di tipo bool
7 bool flag = true;
8
9 //scrivi il valore
10 cout << "vero = " << flag << endl;
11
48
Unitˆ didattica 3 - Introduzione a C++
Prova di esecuzione
3.10 Costanti
In queste prime lezioni si • parlato di variabili come di entitˆ utili per memorizzare valori che posso-
no cambiare durante unÕelaborazione.
Con le costanti si • sicuri che il valore memorizzato resterˆ invariato durante la vita dellÕapplicazione: se
si cerca di assegnare un valore a una costante viene infatti generato un errore in fase di compilazione.
49
Sezione 2 - Primi elementi di programmazione
Codice
Const.cpp
1 #include <iostream>
2 using namespace std;
3 //INIZIO
4 int main ()
5 {
6 //dichiarazione di una costante di tipo double
7 const double PIgreco = 3.1459;
8
9 //scrivi il valore della costante
10 cout << PIgreco << endl;
11
12 //salta due righe
13 cout << endl << endl;
14 //arresta l'esecuzione del programma
15 system ("pause");
16 //termina il programma
17 return 0 ;
18 }
50
Unità didattica 3 - Introduzione a C++
Esercizi
Unità didattica 3
4 Modificare il codice del listato del programma “Ciao.cpp” affinché scriva il vostro nome e cognome an-
ziché il messaggio di benvenuto.
6 Modificare il codice del listato del programma “Ciao.cpp” affinché scriva il messaggio “Benvenuto in la-
boratorio”.
8 Nel seguente codice c’è un errore; individuare il relativo numero di riga e spiegare di che errore
si tratta.
n.r. Ciao.cpp
1 #include <iostream>
2 using namespace std
3 //INIZIO
4 int main ()
5 {
6 //scrivi il messaggio di saluto
7 cout << "Ciao, mondo!" << endl;
8 //arresta l'esecuzione del programma
9 system ("pause");
10 //termina il programma
11 return 0;
12 }
9 Nel seguente codice c’è un errore; individuare il relativo numero di riga e spiegare di che errore
si tratta.
n.r. Ciao.cpp
1 #include <iostream>
2 using namespace std;
3 //INIZIO
4 int main ()
5 {
6 //scrivi il messaggio di saluto
7 cout << "Ciao, mondo! << endl;
8 //arresta l'esecuzione del programma
9 system ("pause");
10 //termina il programma
11 return 0;
12 }
51
Sezione 2 - Primi elementi di programmazione
Esercizi
Unità didattica 3
q0 Nel seguente codice c’è un errore; individuare il relativo numero di riga e spiegare di che errore
si tratta.
n.r. Ciao.cpp
1 #include <iostream>
2 using namespace std;
3 //INIZIO
4 int main ()
5 {
6 //scrivi il messaggio di saluto
7 cout << "Ciao, mondo! " endl;
8 //arresta l'esecuzione del programma
9 system ("pause");
10 //termina il programma
11 return 0 ;
12 }
qa Nel seguente codice c’è un errore; individuare il relativo numero di riga e spiegare di che errore
si tratta.
n.r. Ciao.cpp
1 #include <iostream>
2 using namespace std;
3 //INIZIO
4 int main ()
5 {
6 //scrivi il messaggio di saluto
7 cout << "Ciao, mondo!" << endl;
8 //arresta l'esecuzione del programma
9 system ("pause");
10 //termina il programma
11 return "0" ;
12 }
qs Nella tabella sottostante sono indicate alcune ipotesi di nomi di variabili; ricordando quali sono le rego-
le per la scelta dei nomi di variabili, seleziona i nomi corretti.
qd Nel segmento di codice riportato sotto è presente un errore di sintassi: individualo e correggilo.
integer numero;
numero=122;
cout << numero << endl;
52
Unità didattica 3 - Introduzione a C++
Esercizi
Unità didattica 3
qf Nel segmento di codice riportato sotto è presente un errore di sintassi: individualo e correggilo.
int numero;
numero= 0.122;
cout << numero << endl;
qg Nel segmento di codice riportato sotto è presente un errore di sintassi: individualo e correggilo.
integer numero;
numero = 122;
cout << numero endl;
qh Indica la parola chiave per definire le variabili di tipo intero corto, il minimo e il massimo valore che es-
se possono contenere.
qj Indica la parola chiave per definire le variabili di tipo intero, il minimo e il massimo valore che esse pos-
sono contenere.
qk Indica la parola chiave per definire le variabili di tipo intero lungo, il minimo e il massimo valore che es-
se possono contenere.
ql Indica la parola chiave per definire le variabili di tipo reale a doppia precisione e gli intervalli dei valori
che esse possono contenere.
w0 Indica la parola chiave per definire le variabili di tipo reale a precisione singola e gli intervalli dei valori
che esse possono contenere.
53
Unitˆ didattica
4
Visualizzazione
e acquisizione
CHE COSA IMPARERAI A FARE
$ Istruzioni di acquisizione
$ Formattazione dellÕoutput
$ Caratteri di escape
Unitˆ didattica
Visualizzazione e acquisizione
4
I metodi per la scrittura delle informazioni visti finora sono in grado anche di gestire un sistema di
formattazione dellÕoutput. In questa Unitˆ didattica viene fornita la descrizione di un primo tipo di
formattazione dei risultati dellÕesecuzione dei programmi. Quando si studieranno, invece, gli opera-
tori si avrˆ modo di formattare i risultati in maniera pi• complessa ed efficiente.
LÕistruzione cin >> ... acquisisce quanto digitato sulla tastiera fino a quando lÕu-
tente non preme il tasto Invio.
LÕesecuzione del metodo cin interrompe momentaneamente il flusso di elaborazione del program-
ma e lascia il sistema in attesa finchŽ lÕutente, dopo aver digitato tutti i caratteri desiderati sulla ta-
stiera, preme il tasto Invio.
Codice
Cin.cpp
1 #include <iostream>
2 using namespace std;
3 //INIZIO
4 int main ()
5 {
6 //dichiara una variabile di tipo intero
7 int numero;
8
9 //messaggio per lÕutente
10 cout << "Inserisci un numero => ";
11
12 //assegna a numero il valore letto da tastiera
13 cin >> numero;
14
15 //scrivi la variabile
55
Sezione 2 - Primi elementi di programmazione
Prova di esecuzione
Naturalmente, lÕistruzione cin >> ... permette anche la lettura di numeri di tipo double.
Codice
Cin_double.cpp
1 #include <iostream>
2 using namespace std;
3 //INIZIO
4 int main ()
5 {
6 //dichiara una variabile di tipo double
7 double numero;
8
9 //messaggio per lÕutente
10 cout << "Inserisci un numero => " ;
11
12 //assegna a numero il valore letto da tastiera
56
Unitˆ didattica 4 - Visualizzazione e acquisizione
Prova di esecuzione
Codice
Cin_multiplo.cpp
1 #include <iostream>
2 using namespace std;
3 //INIZIO
4 int main ()
5 {
6 //dichiara le variabili di tipo double
7 double numero1;
8 double numero2;
9 double numero3;
10
57
Sezione 2 - Primi elementi di programmazione
Prova di esecuzione
Codice
Scrivi.cpp
1 #include <iostream>
2 using namespace std;
58
Unitˆ didattica 4 - Visualizzazione e acquisizione
3 //INIZIO
4 int main ()
5 {
6 //dichiara variabili e assegna valori
7 double n1 = 0.01;
8 double n2 = 0.6;
9 double n3 = 30.8;
10
11 //scrivi
12 cout << n1 << endl;
13 cout << n2 << endl;
14 cout << n3 << endl;
15
16 //salta due righe
17 cout << endl << endl;
18 //arresta lÕesecuzione del programma
19 system ("pause");
20 //termina il programma
21 return 0;
22 }
Prova di esecuzione
Che confusione! Come si pu˜ vedere, i numeri non sono incolonnati in modo corretto.
...........................................................................................................................................
59
Sezione 2 - Primi elementi di programmazione
I manipolatori possono essere usati in contemporanea nelle istruzioni di output, come si vede nellÕi-
struzione che segue.
cout << setprecision(2) << setw(8) << x;
Questo comando visualizza il valore della variabile x entro un campo di larghezza 8 caratteri e con
una precisione di due cifre decimali.
NellÕesempio di output che segue • riportata la formattazione che avrebbe lÕoutput del valore Ð34,95
(ogni carattere Ò¥Ó corrisponde a uno spazio).
¥ ¥ -34.95
LÕimpostazione della precisione non influenza i campi interi. Sfortunatamente il metodo set-
precision non consente di visualizzare gli zeri non significativi: per esempio, il valore 0.1
sarebbe comunque visualizzato come 0.1, e non come 0.10. Per ottenere lÕintroduzione degli zeri
non significativi • necessario impostare il cosiddetto formato di visualizzazione f“sso, mediante il
metodo fixed, da chiamarsi come indicato di seguito.
cout << fixed;
Alcuni vecchi compilatori non supportano il manipolatore fixed: in questo caso si pu˜ utilizzare il
pi• oscuro comando indicato di seguito.
cout << setiosflags(ios::fixed);
La combinazione dei tre manipolatori appena visti riesce finalmente a farci ottenere il risultato di for-
mattazione desiderato. Ecco lÕistruzione di output completa.
Fortunatamente, i manipolatori setprecision e fixed devono essere utilizzati una sola volta: lo
stream, infatti, ricorda le impostazioni di formattazione anche per i successivi output. Al contrario,
la chiamata del metodo setw deve essere ripetuta per ciascuna istanza di cout, cio• per ciascun va-
lore da mostrare a video.
Codice
Tabula.cpp
1 #include <iostream>
2 #include <iomanip>
3 using namespace std;
4 //INIZIO
5 int main ()
6 {
7 //dichiara variabili e assegna valori
8 double n1 = 0.01;
9 double n2 = 0.6;
10 double n3 = 30.8;
11
12 //imposta numero cifre decimali
13 cout << setprecision(3);
60
Unitˆ didattica 4 - Visualizzazione e acquisizione
14
15 //imposta uso della virgola "fissa"
16 cout << fixed;
17
18 //imposta ampiezza totale
19 cout << setw(8);
20
21 //scrivi il valore
22 cout << n1 << endl;
23
24 //imposta ampiezza totale
25 cout << setw(8);
26
27 //scrivi il valore
28 cout << n2 << endl;
29
30 //imposta ampiezza totale
31 cout << setw(8);
32
33 //scrivi il valore
34 cout << n3 << endl;
35
36 //salta due righe
37 cout << endl << endl;
38 //arresta lÕesecuzione del programma
39 system ("pause");
40 //termina il programma
41 return 0;
42 }
Prova di esecuzione
61
Sezione 2 - Primi elementi di programmazione
Codice
Escape.cpp
1 #include <iostream>
2 using namespace std;
3 //INIZIO
4 int main ()
5 {
6 //dichiara tre variabili intere e assegna i valori
7 int numero1 = 35;
8 int numero2 = 115;
9 int numero3 = 220;
10
11 /*emetti un suono \a, vai a capo \n e scrivi il valore
12 della variabile numero1*/
13 cout << "\a\nPrimo valore = " << numero1;
14
15 /*vai a capo due volte \n\n, inserisci una tabulazione orizzontale \t
16 e scrivi il valore della variabile numero2*/
17 cout << "\n\n\tSecondo valore = " << numero2;
18
19 /*vai a capo due volte \n\n e scrivi il valore della variabile numero3*/
20 cout << "\n\nTerzo valore = " << numero3;
21
22 //salta due righe
23 cout << endl << endl;
24 //arresta l’esecuzione del programma
25 system ("pause");
26 //termina il programma
27 return 0;
28 }
62
Unitˆ didattica 4 - Visualizzazione e acquisizione
Prova di esecuzione
63
Sezione 2 - Primi elementi di programmazione
Esercizi
Unità didattica 4
3 Supponendo che a fronte della richiesta del programma “Scrivi due numeri”, l’utente digiti
12 21
che cosa viene scritto a video dopo l’esecuzione delle istruzioni seguenti?
int numero1;
int numero2;
cout << "Inserisci due numeri => ";
cin >> numero1 >> numero2;
cout << numero2 << numero1;
oppure
e spiegare perché.
oppure
e spiegare perché.
6 Quale header bisogna inserire nel programma per usare le formattazioni setw, setprecision e
fixed?
7 Le due istruzioni indicate sotto vengono eseguite in sequenza. Che cosa compare a video?
cout << setw(8)<< 5 << endl;
cout << 5 << endl;
64
Unità didattica 4 - Visualizzazione e acquisizione
Esercizi
Unità didattica 4
65
Unitˆ didattica
5
Operatori
A conclusione della trattazione sui vari tipi di dato disponibili in C++, vediamo quali operazioni arit-
metiche possono essere eseguite su di essi.
OPERAZIONE C++
Addizione +
Sottrazione Ð
Moltiplicazione *
Divisione /
Modulo (resto) %
Gli operatori riportati nella tabella vengono definiti operatori binari, perchŽ indi-
cano operazioni effettuabili su due operandi, che possono essere variabili o espres-
sioni.
Addizione e sottrazione
Il modo pi• semplice per eseguire la somma • scrivere lÕistruzione che segue.
somma = numero1 + numero2;
Questa istruzione somma il contenuto della prima variabile con il contenuto della seconda e il risul-
tato dellÕoperazione viene memorizzato nella variabile somma.
Fin qui niente di nuovo. Si possono anche sommare una variabile e un numero, come nellÕistruzio-
ne riportata di seguito.
somma = numero1 + 8;
Quanto indicato per la somma vale, chiaramente, anche per la sottrazione.
Bisogna per˜ fare attenzione ai tipi delle variabili che vengono utilizzate in unÕespressione.
Codice
Somma.cpp
1 #include <iostream>
2 using namespace std;
67
Sezione 2 - Primi elementi di programmazione
3 //INIZIO
4 int main ()
5 {
6 //dichiara e valorizza la prima variabile intera
7 int numero1 = 235;
8
9 //dichiara la seconda variabile intera
10 int numero2 = 265;
11
12 //dichiara la variabile per il risultato
13 int somma;
14
15 //esegui l'operazione
16 somma = numero1 + numero2;
17
18 //scrivi il risultato
19 cout << "Somma = " << somma << endl;
20
21 //salta due righe
22 cout << endl << endl;
23 //arresta l'esecuzione del programma
24 system ("pause");
25 //termina il programma
26 return 0;
27 }
Prova di esecuzione
Moltiplicazione
LÕoperazione di moltiplicazione viene effettuata con lÕausilio dellÕoperatore *. Per moltiplicare due
valori si utilizza la sintassi indicata di seguito.
prod = numero1 * numero2;
La variabile prod contiene il prodotto ottenuto dalla moltiplicazione delle due variabili numero1 e
numero2. Vediamo un esempio pratico.
68
Unitˆ didattica 5 - Operatori
Codice
Prodotto.cpp
1 #include <iostream>
2 using namespace std;
3 //INIZIO
4 int main ()
5 {
6 //dichiara e valorizza la prima variabile intera
7 int numero1 = 150;
8
9 //dichiara e valorizza la seconda variabile intera
10 int numero2 = 20;
11
12 //dichiara la variabile per il risultato
13 int prodotto;
14
15 //esegui l'operazione
16 prodotto = numero1 * numero2;
17
18 //scrivi il risultato
19 cout << "prodotto = " << prodotto << endl;
20
21 / salta due righe
22 cout << endl << endl;
23 //arresta l'esecuzione del programma
24 system ("pause");
25 //termina il programma
26 return 0;
27 }
Prova di esecuzione
Divisione
Per effettuare una divisione si utilizza lÕoperatore /. Per esempio, lÕistruzione
val = numero1 / numero2;
memorizza nella variabile val il risultato della divisione indicata a destra dellÕoperatore =. Se i due
operandi sono di tipo intero il risultato della divisione • anchÕesso di tipo int, mentre se uno dei due
operandi • di tipo double il risultato • di tipo double.
Vediamo un esempio pratico.
69
Sezione 2 - Primi elementi di programmazione
Codice
Divisione.cpp
1 #include <iostream>
2 using namespace std;
3 //INIZIO
4 int main ()
5 {
6 //dichiara e valorizza la prima variabile intera
7 int numero1 = 150;
8
9 //dichiara e valorizza la seconda variabile intera
10 int numero2 = 20;
11
12 //dichiara la variabile per il risultato
13 int divisione;
14
15 //esegui l'operazione
16 divisione = numero1 / numero2;
17
18 //scrivi il risultato
19 cout << "divisione = " << divisione << endl;
20
21 //salta due righe
22 cout << endl << endl;
23 //arresta l'esecuzione del programma
24 system ("pause");
25 //termina il programma
26 return 0;
27 }
Prova di esecuzione
Come si nota nella figura, il risultato non • quello che ci si aspettava: lÕutilizzo di operandi tutti di ti-
po int ha prodotto infatti un troncamento della parte decimale, visualizzando come risultato Ò7Ó
(anzichŽ il valore corretto Ò7,5Ó). Per ottenere il risultato giusto • indispensabile che la variabile che
contiene il risultato della divisione sia dichiarata di tipo double e che almeno una delle altre due
70
Unitˆ didattica 5 - Operatori
(numero1 e/o numero2) sia dichiarata come di tipo double. Vediamo la dimostrazione pratica di
quanto detto, utilizzando alcune variabili di tipo double.
Codice
Divisione_double.cpp
1 #include <iostream>
2 using namespace std;
3 //INIZIO
4 int main ()
5 {
6 //dichiara e valorizza la prima variabile intera
7 int numero1 = 150;
8
9 //dichiara e valorizza la seconda variabile intera
10 double numero2 = 20;
11
12 //dichiara la variabile per il risultato
13 double divisione;
14
15 //esegui l'operazione
16 divisione = numero1 / numero2;
17
18 //scrivi il risultato
19 cout << "divisione = " << divisione;
20
21 //salta due righe
22 cout << endl << endl;
23 //arresta l'esecuzione del programma
24 system ("pause");
25 //termina il programma
26 return 0;
27 }
Prova di esecuzione
71
Sezione 2 - Primi elementi di programmazione
Operatore modulo
Le quattro operazioni appena studiate consentono di effettuare calcoli di qualsiasi genere ma, a
volte, pu˜ essere necessario conoscere solo il resto di una divisione. A questo scopo il C++ mette
a disposizione lÕoperatore modulo, rappresentato dal simbolo %. Esso viene utilizzato come lÕo-
peratore di divisione /, solo che il risultato dellÕoperazione • il resto del quoziente. Per esempio,
lÕistruzione
resto = numero1 % numero2;
consente di memorizzare nella variabile resto il resto della divisione indicata a destra dellÕoperatore =.
Codice
Resto.cpp
1 #include <iostream>
2 using namespace std;
3 //INIZIO
4 int main ()
5 {
6 //dichiara e valorizza la prima variabile intera
7 int numero1 = 150;
8
9 //dichiara e valorizza la seconda variabile intera
10 int numero2 = 20;
11
12 //dichiara la variabile per il risultato
13 int resto;
14
15 //esegui l'operazione
16 divisione = numero1 % numero2;
17
18 //scrivi il risultato
19 cout << "resto = " << resto;
20
21 //salta due righe
22 cout << endl << endl;
23 //arresta l'esecuzione del programma
24 system ("pause");
25 //termina il programma
26 return 0;
27 }
Prova di esecuzione
72
Unitˆ didattica 5 - Operatori
Codice
Imponibile.cpp
1 #include <iostream>
2 using namespace std;
73
Sezione 2 - Primi elementi di programmazione
3 //INIZIO
4 int main ()
5 {
6 //definisci la variabile importo
7 double importo;
8
9 //definisci la variabile IVA
10 double IVA;
11
12 //definisci la costante per la percentuale IVA
13 const int PERC=20;
14
15 //richiedi il valore dell'importo
16 cout << "\nInserisci l'importo ";
17
18 //leggi importo
19 cin >> importo;
20
21 //calcola l'IVA sull'importo
22 //N.B.: l'uso delle parentesi in questo caso • facoltativo
23 IVA = (importo*PERC)/100;
24
25 //aggiungi IVA
26 importo += IVA;
27
28 //scrivi l'importo con IVA
29 cout << "\nImporto con IVA = " << importo;
30
31 //salta due righe
32 cout << endl << endl;
33 //arresta l'esecuzione del programma
34 system ("pause");
35 //termina il programma
36 return 0;
37 }
Prova di esecuzione
74
Unitˆ didattica 5 - Operatori
Operatori unari
Il C++, oltre a mettere a disposizione gli operatori binari visti in precedenza, dispone di particolari
operatori utilizzabili su una singola variabile, i quali permettono lÕincremento (o il decremento) di
una unitˆ del valore contenuto nella variabile: si tratta degli operatori unari.
Per esempio, per incrementare la variabile numero si utilizza lÕoperatore di incremento ++, allÕin-
terno del costrutto
++numero;
che equivale allÕistruzione indicata di seguito.
numero = numero + 1;
Il medesimo concetto • da applicarsi al decremento di una variabile, mediante lÕoperatore di decre-
mento --. LÕistruzione
--numero;
equivale a quella indicata di seguito.
numero = numero - 1;
Codice
Unario.cpp
1 #include <iostream>
2 using namespace std;
3 //INIZIO
4 int main ()
5 {
6 //dichiara due variabili intere
7 //e ponile = 0
8 int num1=0;
9 int num2=0;
10
11 //scrivi i loro valori iniziali
12 cout << "\n\nVALORI INIZIALI\n";
13 cout << "num1 = " << num1 << "\n";
14 cout << "num2 = " << num2 << "\n";
15
16 //incrementa la prima
17 ++num1;
18
19 //decrementa la seconda
20 --num2;
21
22 //scrivi i loro valori finali
23 cout << "\n\nVALORI FINALI\n";
24 cout << "num1 = " << num1 << "\n";
25 cout << "num2 = " << num2 << "\n";
26
27 //salta due righe
75
Sezione 2 - Primi elementi di programmazione
Prova di esecuzione
76
Unitˆ didattica 5 - Operatori
Codice
Pre_e_post.cpp
1 #include <iostream>
2 using namespace std;
3 //INIZIO
4 int main ()
5 {
6 //dichiara e inizializza una variabile intera
7 int num = 10;
8
9 //incremento post-fisso
10 cout << "num con incremento post-fisso = " << num++ << "\n";
11
12 //ripristina num
13 num = 10;
14
15 //incremento pre-fisso
16 cout << "num con incremento pre-fisso = " << ++num <<"\n";
17
18 //salta due righe
19 cout << endl << endl;
20 //arresta l'esecuzione del programma
21 system ("pause");
22 //termina il programma
23 return 0;
24 }
Prova di esecuzione
77
Sezione 2 - Primi elementi di programmazione
é importante tenere bene a mente queste differenze operative per non incappare in errori che, a vol-
te, possono risultare difficili da scovare.
Il risultato di unÕoperazione di confronto pu˜ assumere solo due valori, quindi • di tipo booleano:
vero o falso (True o False).
Il C++ mette a disposizione gli operatori relazionali indicati nella tabella che segue.
OPERATORE DESCRIZIONE
== Uguale a
< Minore di
> Maggiore di
<= Minore o uguale a
>= Maggiore o uguale a
!= Diverso
Esempio Test.......................................................................................................................
Verificare se due numeri acquisiti da tastiera sono uguali.
Codice
Test.cpp
1 #include <iostream>
2 using namespace std;
3 //INIZIO
4 int main ()
5 {
6 //definisci due variabili intere
7 int num1;
8 int num2;
9
10 //dichiara una variabile per il confronto
11 bool test;
12
78
Unitˆ didattica 5 - Operatori
Prove di esecuzione
Nella prima esecuzione i due numeri inseriti sono diversi e il contenuto della variabile booleana
test risulta essere 0 (che corrisponde a false), mentre nella seconda prova i due numeri digitati
sulla tastiera sono uguali e, quindi, test contiene 1 (true).
79
Sezione 2 - Primi elementi di programmazione
Alla riga 11 • stata dichiarata la variabile test di tipo bool, che contiene il risultato (booleano) del
confronto tra num1 e num2 (num1==num2;).
LÕistruzione test = num1==num2; verifica, tramite lÕoperatore di uguaglianza, che i due valo-
ri inseriti siano uguali e memorizza il risultato nella variabile booleana test. Infine, alla riga 25
viene visualizzato il risultato del confronto.
...........................................................................................................................................
C++ mette a disposizione gli operatori logici riportati nella tabella che segue.
OPERATORE DESCRIZIONE
& AND (completo)
&& AND (cortocircuito)
| OR (completo)
|| OR (cortocircuito)
! NOT (negazione)
^ XOR
80
Unitˆ didattica 5 - Operatori
Codice
TestAnd.cpp
1 #include <iostream>
2 using namespace std;
3 //INIZIO
4 int main ()
5 {
6 //dichiara una variabile intera
7 int num;
8
9 //dichiara una variabile per il test
10 bool test;
11
12 //chiedi e leggi un numero
13 cout << "\nInserisci un numero ";
14 cin >> num;
15
16 //esegui il test
17 test = (num>=10) & (num<=100);
18
19 //scrivi il risultato
20 cout << "\n\nTest = "<< test << "\n";
21
22 //salta due righe
23 cout << endl << endl;
24 //arresta l'esecuzione del programma
25 system ("pause");
26 //termina il programma
27 return 0;
28 }
81
Sezione 2 - Primi elementi di programmazione
Prova di esecuzione
Per concludere, come è descritto nella tabella di verità, l’uso dell’operatore & consente di avere co-
me risultato true solo se entrambe le espressioni booleane sono true.
L’operatore logico & viene anche definito operatore logico di valutazione completa, in quanto
può mettere in relazione tutte le diverse espressioni booleane.
Operatore OR ( | )
Questo operatore valuta due espressioni booleane e re- OP1 OP2 OP1 | OP2
stituisce true se almeno una di esse è true. Dalla ta-
bella di verità riportata a destra si evince che solo se en- false false false
trambe le espressioni risultano non verificate (false) il false true true
risultato del confronto è false, mentre è true in tutti true false true
gli altri casi. E true true true
82
Unitˆ didattica 5 - Operatori
Codice
TestOr.cpp
1 #include <iostream>
2 using namespace std;
3 //INIZIO
4 int main ()
5 {
6 //dichiara due variabili booleane e assegna due valori opposti
7 bool OP1 = true;
8 bool OP2 = false;
9
10 //visualizza i valori delle variabili
11 cout << "\nOperatore 1 = " << OP1;
12 cout << "\nOperatore 2 = " << OP2;
13
14 //scrivi il risultato
15 cout << "\n\nOP1 or OP2 = " << (OP1 | OP2);
16
17 //salta due righe
18 cout << endl << endl;
19 //arresta l'esecuzione del programma
20 system ("pause");
21 //termina il programma
22 return 0;
23 }
Prova di esecuzione
83
Sezione 2 - Primi elementi di programmazione
OP1 OP1
false true
true false
LÕoperando pu˜ essere una singola variabile oppure unÕespressione booleana, come si pu˜ vedere
nelle due istruzioni di esempio riportate di seguito.
bool test1 = !test2;
bool test1= !(num1>=num2);
Codice
TestXor.cpp
1 #include <iostream>
2 using namespace std;
3 //INIZIO
4 int main ()
5 {
6 //dichiara due variabili booleane e assegna due valori uguali
84
Unitˆ didattica 5 - Operatori
Prova di esecuzione
85
Sezione 2 - Primi elementi di programmazione
Esercizi
Unità didattica 5
3 Che cosa c'è di sbagliato nella seguente versione della formula risolutiva dell'equazione di secondo grado?
xl = (-b - sqrt(b*b-4*a*c))/2*a;
x2 = (-b + sqrt(b*b-4*a*c))/2*a;
// sqrt() = radice quadrata
4 Date le istruzioni:
int n;
double x = 5.5;
n = x;
86
Unità didattica 5 - Operatori
Esercizi
Unità didattica 5
qa Scrivere un programma che richieda all'utente due valori interi, e che calcoli e visualizzi:
: la somma;
: la differenza;
: il prodotto;
: la media.
quanto vale test per i valori di numero1 e numero2 indicati nella tabella?
ql Nel seguente codice, che controlla se un numero è compreso in un intervallo tra 25 e 90, c’è un errore
logico: quale?
w0 Dare il resto. Implementare un programma che aiuti un cassiere a stabilire il resto dovuto. Il programma
prevede due dati in input: la somma dovuta e la quantità di denaro ricevuta dal cliente. Il programma cal-
cola la differenza e stabilisce gli euro da restituire al cliente.
87
Sezione 2 - Primi elementi di programmazione
Esercizi
Unità didattica 5
N1 N2 TEST
3 5
3 3
5 3
N1 N2 TEST
3 5
3 3
5 3
N1 N2 TEST
3 5
3 3
5 3
wf Che cosa scrive in output il frammento di codice seguente (le variabili sono tutte di tipo int)?
x = 2;
y = x + x;
t = y + x;
cout << t;
88
Sezione 3
Organizzazione
degli algoritmi
†
Obiettivi ◊ Strutturare un algoritmo in modo logicamente corretto
◊ Applicare i principi base della programmazione
strutturata
◊ Trasferire nel linguaggio di programmazione
le strutture fondamentali di un algoritmo
Infatti, il primo tipo di problema richiede il possesso di conoscenze intuitive non formalizzabili,
mentre il secondo non fornisce un numero sufficiente di informazioni iniziali.
Colui che si propone di risolvere il problema, cioè il risolutore, deve svolgere un’attività creativa nel-
la ricerca della soluzione al problema e, avvalendosi di un patrimonio di conoscenze precedente-
mente acquisite, deve:
1. interpretare l’enunciato del problema;
2. evidenziare i dati che l’enunciato fornisce (dati iniziali);
3. ricercare tutte le azioni (operazioni) da compiere sui dati e collegarle logicamente in funzione
del raggiungimento dei risultati o dati finali;
4. eseguire nell’ordine le operazioni descritte al punto 3;
5. verificare i risultati.
L’attività che il risolutore svolge per realizzare i punti 1), 2), 3) prende il nome di analisi
del problema. Quando il risolutore realizza il punto 4), cioè quando esegue le azioni
descritte nel punto 3), assume il ruolo di esecutore.
Spesso, il risolutore e l’esecutore sono la stessa persona ma, a volte, non è così: può presentarsi il
caso che il risolutore demandi l’esecuzione della soluzione del problema a un’altra persona oppu-
re a un esecutore automatico quale, per esempio, un calcolatore elettronico. In questo caso, il ri-
solutore deve preparare un’accurata descrizione del procedimento risolutivo costituito da un in-
sieme di comandi da impartire all’esecutore. Tale descrizione è, naturalmente, condizionata dalle
caratteristiche dell’esecutore.
91
Sezione 3 - Organizzazione degli algoritmi
Problema
Risolutore
Procedimento Analisi
risolutivo del problema
Esempio Televisore.............................................................................................................
Algoritmo per l’uso di un apparecchio televisivo.
Pseudocodifica
1 INIZIO
2 oggetti da manipolare: televisore, telecomando;
3 premere il tasto di accensione;
4 selezionare un canale con il telecomando;
92
Unità didattica 6 - Algoritmi e pseudocodifica
Osserviamo che l’algoritmo descrive il comportamento che un esecutore deve tenere per risolvere il
problema.
Possiamo anche dire che è stata fatta la descrizione di un programma di comportamento, in cui le
azioni sono espresse in modo imperativo e rappresentano dei comandi; inoltre, sono presenti azioni
che vengono eseguite solo se si verificano determinate condizioni come, per esempio:
5 se il programma del canale selezionato non è gradito,
6 allora ripetere l’azione 4
7 altrimenti assistere alla trasmissione;
Frasi come questa descrivono delle azioni, dette di controllo, che impongono delle condizioni (pro-
gramma non gradito) il cui verifìcarsi o meno determina il comando di esecuzione di una sequenza
di azioni piuttosto che di un’altra.
La frase:
premere il tasto di accensione
descrive, quindi, un comando o istruzione.
Le istruzioni sono state contrassegnate da numeri per poterle individuare meglio.
Le istruzioni contrassegnate dai numeri 1 e 10 avvertono l’esecutore quando inizia e quando termi-
na la sequenza delle istruzioni, rispettivamente.
La descrizione di una esecuzione dell’algoritmo da parte di un esecutore potrebbe essere la seguente:
1. l’esecutore preme il tasto di accensione;
2. seleziona il primo canale con il telecomando;
3. assiste alla trasmissione;
4. la trasmissione è terminata e l’esecutore spegne il televisore.
93
Sezione 3 - Organizzazione degli algoritmi
Dunque, mentre la descrizione dell’algoritmo è unica, l’esecutore può realizzare più esecuzioni e
non è detto che tutte le esecuzioni siano uguali.
Per esempio, l’azione 2 della procedura potrebbe essere così eseguita:
l’esecutore seleziona il secondo canale con il telecomando.
È evidente che l’esecutore deve avere le seguenti competenze:
6.3 Algoritmi
Nell’algoritmo per l’uso di un apparecchio televisivo appena descritto, gli oggetti da manipolare (ta-
sti, telecomando) sono concreti e non formalizzabili.
Descriviamo ora una procedura in cui gli oggetti da manipolare sono astratti e formalizzabili.
Pseudocodifica
INIZIO
dati iniziali: b e h;
moltiplica b per h;
dividi il prodotto per 2 e ottieni l’area: area = (b × h) / 2;
comunica il risultato, area;
FINE
...........................................................................................................................................
Questo algoritmo vale per calcolare l’area di qualsiasi triangolo purché si conoscano le misure della
base e dell’altezza. In questo caso, si dice che l’algoritmo risolve una classe di problemi.
L’insieme di tutti i problemi che vengono risolti dallo stesso algoritmo costituisce una
classe di problemi.
L’algoritmo che descrive l’uso dell’apparecchio televisivo e quello per il calcolo dell’area del triango-
lo presentano una differenza sostanziale: il primo opera su oggetti fisici (il televisore e il telecoman-
do) facilmente individuabili e, quindi, per essi non è necessario dare una descrizione dettagliata; il se-
condo opera su oggetti che non sono fisici ma astrazioni mentali (segmenti, figure piane) dalla cui
rappresentazione simbolica (numeri interi o decimali) non si può prescindere nella descrizione della
procedura risolutiva.
In generale, un algoritmo non serve mai per risolvere un singolo problema ma per risolvere più pro-
blemi che differiscono solo per i dati iniziali.
94
Unitˆ didattica 6 - Algoritmi e pseudocodifica
Per algoritmo si intende una successione finita di passi contenenti le istruzioni che
specificano le operazioni da compiere per risolvere una classe di problemi.
Quasi tutti gli algoritmi, tranne forse i pi• semplici, ricevono dei dati, li trasformano e, quindi, li co-
municano allÕutente. Un programma di elaborazione testi riceve i propri dati sotto forma di parole;
tali parole vengono formattate dal programma per dare loro un aspetto gradevole, quindi il pro-
gramma le stampa ordinatamente su carta.
Un archivio anagrafico riceve i propri dati sotto forma di nomi, indirizzi e numeri telefonici, memo-
rizza le informazioni sui propri supporti magnetici e poi visualizza i dati in un formato considerato
utile per lÕutente. Un sistema di guida per missili nucleari riceve i propri dati sotto forma di coordi-
nate del bersaglio.
Qualunque programma che abbia un minimo di funzionalitˆ segue queste tre fasi fondamentali:
La fase di acquisizione dei dati viene anche definita fase di input o di immissione. La
fase di presentazione dei risultati prende il nome di output o di emissione.
In un computer, lo strumento attraverso il quale avviene la maggior parte delle operazioni di input •
la tastiera (standard input) mentre lÕunitˆ pi• utilizzata per la presentazione dei risultati • il video
(standard output); questo processo • schematizzato nella figura seguente.
I dati di input sono i valori iniziali che servono allÕalgoritmo per elaborare la soluzione
del problema.
Le variabili di lavoro sono dati non provenienti dallÕesterno, bens“ rappresentano ri-
sultati intermedi ottenuti durante lÕelaborazione.
I dati di output costituiscono il risultato delle operazioni effettuate dallÕalgoritmo. Essi
vengono resi disponibili allÕesterno mediante visualizzazione o stampa.
95
Sezione 3 - Organizzazione degli algoritmi
Dati Dati
ELABORAZIONE
di input di output
96
Unitˆ didattica 6 - Algoritmi e pseudocodifica
mere le varie azioni in modo univoco e il pi• possibile conciso. Ma il linguaggio naturale, a causa del-
la sua complessitˆ e ambiguitˆ, non • sempre adatto per descrivere in modo adeguato algoritmi, so-
prattutto quando questi non sono semplici come quelli trattati finora. é, quindi, necessario costrui-
re un linguaggio che consenta una giusta interpretazione delle istruzioni da eseguire da parte dellÕo-
peratore, soprattutto se questo • una macchina, come nel caso di un elaboratore elettronico. DÕora
in avanti, introdurremo gradualmente il linguaggio della pseudocodifica.
LÕistruzione mediante la quale a una variabile viene attribuito, cio• assegnato, un va-
lore appartenente a un dato insieme, detto insieme di definizione della variabile,
prende il nome di istruzione di assegnamento.
5 5 7
A A
A 5 A 7
97
Sezione 3 - Organizzazione degli algoritmi
Nome A
Non si deve mai confondere il contenitore con il contenuto (cioè con il valore assegnato alla variabile).
Elenchiamo alcuni tipi di assegnamento:
Si deve sempre tenere presente che se a una variabile viene assegnato un valore, allora il valore prece-
dentemente contenuto viene distrutto.
Una variabile, prima di essere trattata, deve contenere già un valore, ovvero essere
già stata inizializzata.
Esempi ................................................................................................................................
Data la sequenza di istruzioni:
a ← 5;
b ← a + 1;
c ← a + b;
dopo l’esecuzione della terza istruzione il contenuto di ogni variabile è: a = 5; b = 6; c = 11.
Data la sequenza di istruzioni:
a ← 5;
b ← a + c;
c ← a + 1;
dall’esame della sequenza notiamo che nella seconda istruzione non è possibile assegna-
re un valore alla variabile b perché non è conosciuto il contenuto di c: la variabile c non è
stata inizializzata, cioè non le è stato assegnato alcun valore.
...........................................................................................................................................
98
Unitˆ didattica 6 - Algoritmi e pseudocodifica
In pseudocodifica, si conviene di esprimere tale istruzione con la parola ÒleggiÓ seguita dal nome del-
la variabile racchiusa tra parentesi tonde:
leggi (nome della variabile);
Per esempio, se la variabile • a, si scrive:
leggi (a);
Se le variabili sono pi• di una, aventi per esempio i nomi a, b, c, lÕistruzione di lettura pu˜ essere co-
s“ denotata:
leggi (a, b, c);
LÕistruzione termina con un punto e virgola per far capire allÕesecutore che lÕistruzione • terminata
e ne segue unÕaltra.
In pseudocodifica, questa istruzione viene descritta con la parola ÒscriviÓ seguita dal nome della va-
riabile contenente il risultato, compresa entro parentesi tonde:
scrivi (nome della variabile);
ma il risultato pu˜ anche essere rappresentato da una o pi• espressioni e, quindi, si ha:
scrivi (espress1, espress2, ..., espressN);
Per esempio, nellÕistruzione:
scrivi (a, a Ð b, 10);
se 15 e 3 sono rispettivamente i valori di a e b, lÕesecutore comunica:
15 12 10
Spesso, per rendere comprensibili i risultati da parte dellÕutente, si usano frasi rac-
chiuse tra apici, dette costanti letterali.
99
Sezione 3 - Organizzazione degli algoritmi
Pseudocodifica
input : b, h
output : area
INIZIO
leggi (b, h)
area ← b * h
scrivi (“Area del rettangolo = ”, area)
FINE
...........................................................................................................................................
Ogni algoritmo può essere trasformato in un algoritmo equivalente che faccia uso
solo di alcune strutture di controllo.
Tali strutture di controllo sono:
: sequenza;
: alternativa;
: ripetizione.
Due algoritmi sono equivalenti quando, per gli stessi dati di ingresso, producono i medesimi risultati in
uscita.
Nei prossimi paragrafi daremo una definizione precisa di sequenza, alternativa e ripetizione. Gli
algoritmi descritti nel rispetto del teorema enunciato prendono il nome di algoritmi strutturati e
il loro studio si situa nel processo di razionalizzazione e standardizzazione logica iniziato negli an-
ni ’60.
Le strutture di controllo per creare algoritmi strutturati sono descritte in pseudocodifica mediante
espressioni linguistiche che esprimono in modo sintetico il significato di tali strutture.
Ogni struttura è considerata come un blocco di istruzioni avente una sola entrata e una sola uscita.
100
Unitˆ didattica 6 - Algoritmi e pseudocodifica
é ovvio che, per costruire un programma sicuro che non si blocchi al secondo tentativo di esecuzio-
ne, risulta fondamentale organizzare in modo razionale lÕalgoritmo risolutivo e, allo scopo, • fonda-
mentale il ricorso alla pseudocodifica e al rispetto del teorema di Jacopini-Bš hm.
Inoltre, da ogni algoritmo realizzato in pseudocodifica pu˜ essere derivato un programma in un
qualsiasi linguaggio, mentre un programma costruito senza rispettare il teorema di Jacopini-Bš hm
non pu˜ essere ÒtradottoÓ in pseudocodifica e rende difficoltoso il controllo della sua strategia riso-
lutiva (ammesso che ne abbia una).
Struttura di sequenza
Lo schema della struttura di sequenza di un algoritmo • indicato di seguito: ogni elemento Bi pu˜
rappresentare unÕistruzione semplice (come, per esempio, leggi, scrivi, assegna, ...) ed • detto blocco
semplice, oppure pu˜ essere costituito, a sua volta, da una delle strutture fondamentali (sequenza, al-
ternativa e ripetizione) e, in questo caso, • detto blocco composto.
La pseudocodifica della struttura di sequenza ha la seguente sintassi:
INIZIO
B1
B2
...
...
Bn
FINE
Tornando allÕesempio ÒRettangoloÓ, le istruzioni comprese tra INIZIO e FINE sono indicate in se-
quenza e lÕesecutore deve eseguirle nellÕordine in cui sono disposte.
Pseudocodifica
input : b, h
output : area
INIZIO
leggi (b, h)
area ← b * h
scrivi (ÒArea del rettangolo = Ó, area)
FINE
101
Sezione 3 - Organizzazione degli algoritmi
Esempio Verifica.................................................................................................................
Decidere quanto studiare nellÕeventualitˆ di una verifica.
Pseudocodifica
INIZIO
SE devi fare la verifica
ALLORA
ripassa gli argomenti vecchi
ALTRIMENTI
studia lezione del giorno
FINE SE
FINE
Le due parole INIZIO e FINE delimitano il blocco di istruzioni di cui • formato lÕintero programma.
Le righe dopo ALLORA e dopo ALTRIMENTI corrispondono, rispettivamente, ai blocchi B1 e B2 indicati nel-
lo schema sintattico precedente. In questo caso, entrambi i blocchi sono composti da una sola istruzione.
...........................................................................................................................................
Pseudocodifica
input : a
output : assoluto
INIZIO
leggi (a)
SE a >= 0
ALLORA
assoluto ← a
ALTRIMENTI
assoluto ← Ða
FINE SE
scrivi (Òvalore assoluto = Ó, assoluto)
FINE
102
Unitˆ didattica 6 - Algoritmi e pseudocodifica
Trattandosi di un esempio numerico, nelle prime due righe sono state definite le variabili su cui opera il
programma, distinguendo tra le variabili di input e quelle di output.
...........................................................................................................................................
SE condizione
ALLORA
B1
FINE SE
Esempio Ombrello..............................................................................................................
Decidere se uscire con lÕombrello.
Pseudocodifica
INIZIO
SE piove
ALLORA
esci con lÕombrello
FINE SE
FINE
...........................................................................................................................................
Pseudocodifica
input : a
output : assoluto
INIZIO
leggi (a)
assoluto ← a
SE a < 0
ALLORA
assoluto ← Ða
FINE SE
scrivi (Òvalore assoluto = Ó, assoluto)
FINE
...........................................................................................................................................
LÕesempio successivo mostra come • possibile combinare tra loro diverse strutture di selezione: si
parla in questo caso di strutture di selezione nidificate.
103
Sezione 3 - Organizzazione degli algoritmi
Pseudocodifica
INIZIO
SE data odierna è pari
ALLORA
SE la targa finisce con una cifra pari
ALLORA
usa l’auto
ALTRIMENTI
esci a piedi
FINE SE
ALTRIMENTI
SE la targa finisce con una cifra dispari
ALLORA
usa l’auto
ALTRIMENTI
esci a piedi
FINE SE
FINE SE
FINE
Si noti che le istruzioni (nell’esempio “usa l’auto” e “esci a piedi”) sono scritte rientrate rispetto alle pa-
role ALLORA e ALTRIMENTI e che queste ultime occupano da sole un’intera riga. Inoltre le parole delle
coppie SE-FINE SE e ALLORA-ALTRIMENTI sono sempre incolonnate tra loro. Da ultimo, la parola SE deve
sempre avere il corrispondente FINE SE, mentre è possibile trovare delle strutture di alternativa che ri-
chiedono soltanto le parole SE-ALLORA-FINE SE, senza, dunque, l’alternativa ALTRIMENTI.
...........................................................................................................................................
Ripetizione precondizionale
Il blocco B viene eseguito una o più volte MENTRE la condizione è vera; quando questa diventa fal-
sa, la ripetizione si interrompe.
Il blocco B potrebbe anche non essere mai eseguito se la condizione non è mai vera. Chiariamo quan-
to detto con due esempi.
104
Unitˆ didattica 6 - Algoritmi e pseudocodifica
Pseudocodifica
INIZIO
MENTRE semaforo rosso
Aspetta
FINE MENTRE
Attraversa
FINE
Cos“ come la struttura di alternativa • racchiusa tra le parole SE e FINE SE, anche lÕiterazione ha una pa-
rola iniziale, che • MENTRE, e parole di chiusura, ossia FINE MENTRE. Tali parole vanno incolonnate.
LÕiterazione continua se la condizione indicata dopo MENTRE risulta vera; quando la condizione di-
venta falsa lÕesecuzione del programma procede con lÕistruzione presente subito dopo le parole FINE
MENTRE.
...........................................................................................................................................
Analisi
I numeri da sommare sono: 1, 2, 3, 4.
Data la variabile somma, inizializzata con zero (somma ← 0) deve essere eseguita la seguente se-
quenza di istruzioni:
1 somma ← somma + 1
2 somma ← somma + 2
3 somma ← somma + 3
4 somma ← somma + 4
con lÕistruzione n. 4 la variabile somma contiene il risultato.
Notiamo che le quattro istruzioni sono tutte del tipo:
somma ← somma + k
dove k • una variabile a cui assegnare, di volta in volta, i valori 1, 2, 3, 4.
Pseudocodifica
output : somma
variabile di lavoro : k
INIZIO
somma ← 0
k ← 1;
MENTRE k <= 4
somma ← somma + k
k←k+1
FINE MENTRE
scrivi (Òrisultato = Ó, somma)
FINE
105
Sezione 3 - Organizzazione degli algoritmi
...........................................................................................................................................
Il blocco B viene eseguito una o più volte FINCHÉ la condizione è falsa; quando la condizione di-
venta vera, la ripetizione si interrompe: il blocco B viene quindi ripetuto fino a quando la condizio-
ne indicata dopo FINCHÉ da falsa diventa vera. In questa ripetizione, il blocco B viene eseguito al-
meno una volta perché il controllo viene dopo il blocco B, a differenza della ripetizione MENTRE
nella quale il blocco di controllo viene prima del blocco di istruzioni da eseguire.
Analisi
Il problema si risolve concretamente scoprendo una carta alla volta e contando le carte che man ma-
no vengono scoperte. Se si vuole formalizzare l’algoritmo, una soluzione può essere la seguente.
Pseudocodifica
INIZIO
conta ← 0
RIPETI
scopri una carta
conta ← conta + 1
FINCHÉ è l’asso di cuori
Scrivi (“L’asso di cuori si trova alla posizione = ”, conta)
FINE
Anche in questo caso le istruzioni da ripetere sono racchiuse da una parola iniziale (RIPETI) e da una pa-
rola finale (FINCHÉ).
...........................................................................................................................................
106
Unità didattica 6 - Algoritmi e pseudocodifica
Pseudocodifica
output : somma
variabile di lavoro : k
INIZIO
somma ← 0
k←4
RIPETI
somma ← somma + K
k←k–1
FINCHÉ k = 0
scrivi (“risultato = ”, somma)
FINE
Negli esempi relativi alla somma dei primi 4 numeri, possiamo notare che nelle due strutture di ripe-
tizione MENTRE-FINE MENTRE e RIPETI-FINCHÉ sono sempre presenti:
: una (ma in molti casi anche più di una) variabile di controllo inizializzata prima del ciclo, che
è k, inizializzata con k ← 0 nel ciclo MENTRE e con k ← 4 nel ciclo RIPETI;
: una o più istruzioni che devono essere eseguite una o più volte, tra le quali c’è sempre
una istruzione di modifica della variabile di controllo (k ← k + 1, k ← k –1, rispettiva-
mente);
: la condizione di uscita dal ciclo, falsa per MENTRE e vera per FINCHÉ;
Le due iterazioni si differenziano perché in MENTRE la condizione viene controllata prima di ogni
ciclo e in RIPETI tale controllo viene eseguito dopo l’esecuzione di ogni ciclo. Inoltre, se in MEN-
TRE il blocco di istruzioni potrebbe non essere mai eseguito, in RIPETI il blocco è eseguito alme-
no una volta.
...........................................................................................................................................
107
Sezione 3 - Organizzazione degli algoritmi
: Ogni algoritmo deve iniziare con la parola INIZIO e finire con la parola FINE.
: La selezione è descritta dal costrutto SE ... ALLORA ... ALTRIMENTI ... FINE SE.
: La ripetizione precondizionale è descritta dal costrutto MENTRE ... FINE MENTRE.
: La ripetizione postcondizionale è descritta dal costrutto RIPETI ... FINCHÉ.
: È possibile assegnare a un blocco di istruzioni un nome che ne descrive le funzionalità,
che potrà essere costituito anche da più parole: l’iniziale di ciascuna parola dovrà
essere maiuscola.
: Per le strutture più complesse, che verranno descritte più avanti, si possono usare
anche i costrutti SCEGLI ... CASO ... FINE SCEGLI; PER ... DA ... A ... PASSO ... FINE PER.
Nelle strutture di iterazione, le parole MENTRE e FINCHÉ sono seguite da una condizione.
Anche se queste due parole hanno una funzione simile, il loro significato è ben diverso e va chiarito
con precisione.
MENTRE richiede che la ripetizione sia continuata quando la condizione che la segue è vera; la ri-
petizione si arresti quando la condizione è falsa.
FINCHÉ richiede che la ripetizione sia continuata quando la condizione che la segue è falsa; la ripe-
tizione si arresti quando la condizione è vera.
Nella ripetizione precondizionale, la condizione che indica se si devono eseguire le istruzioni da ite-
rare è posta all’inizio; ne consegue che l’iterazione può non essere eseguita mai (se la condizione in-
dicata dopo MENTRE non è verificata). Nella ripetizione postcondizionale la condizione che indi-
ca se si devono eseguire le istruzioni da ripetere è posta alla fine e, quindi, le istruzioni da iterare ven-
gono sempre eseguite almeno una volta. Le parole MENTRE e FINCHÉ possono essere scambiate,
se la condizione che le segue è sostituita con la sua negazione. L’esempio dell’algoritmo della ricer-
ca dell’asso di cuori in un mazzo di carte può avere anche la seguente versione.
INIZIO
conta ← 0
RIPETI
Scopri una carta
conta ← conta + 1
MENTRE non è l’asso di cuori
Comunica conta
FINE
In sintesi, valgono le seguenti uguaglianze MENTRE = FINCHÉ non e FINCHÉ = MENTRE non.
L’algoritmo in pseudocodifica si presenta come uno schema che può essere codificato senza sforzo
in un qualsiasi linguaggio di programmazione: per questo la sua costruzione riveste un’importanza
primaria. Concludendo, l’algoritmo descritto in modo strutturato rappresenta l’anello di congiun-
zione tra l’analisi del problema e la stesura del codice del programma risolutivo, come si può vedere
nella figura seguente.
PROGRAMMATORE ELABORATORE
108
Unità didattica 6 - Algoritmi e pseudocodifica
Esercizi
Unità didattica 6
1 Si consideri l’algoritmo:
// input
a
// output
assoluto
INIZIO
leggi (a)
SE a >= 0
ALLORA
assoluto <-- a
ALTRIMENTI
assoluto <-- –a
FINE SE
scrivi (“valore assoluto = “, assoluto)
FINE
se a = –39, quale istruzione di assegnamento viene eseguita?
2 Descrivere l'algoritmo che, dati due interi a, b, scriva il valore assoluto della differenza tra il primo e il
secondo numero.
109
Sezione 3 - Organizzazione degli algoritmi
Esercizi
Unità didattica 6
scrivi(i)
FINCHE’ i >= 5
Per gli esercizi dal numero 6 al numero 10 indicare i dati di input, di output e le variabili di lavoro e strut-
turare l’algoritmo risolutivo.
7 Dati i coefficienti di un'equazione di secondo grado, calcolare, se esistono, le due soluzioni reali.
8 Data in input una serie di numeri contare quelli pari e quelli dispari; comunicare anche la percentuale sul
totale dei pari e dei dispari. Suggerimento: si devono ripetere le seguenti istruzioni.
leggi numero
SE numero è pari
ALLORA conta pari
ALTRIMENTI conta dispari
FINE SE
Chiedi “serie finita ?”
leggi risposta
9 Scrivere un programma che legge una serie di numeri di tipo double e, alla fine, comunicare la media dei
numeri.
qa Dato un elenco di persone di cui sono indicati il nome e l’anno di nascita, scrivere il nome del più vec-
chio e del più giovane.
qs Dati due numeri interi e positivi calcolare il loro quoziente intero utilizzando solo le operazioni di somma
e differenza.
110
Unità didattica 6 - Algoritmi e pseudocodifica
Esercizi
Unità didattica 6
qf Date le coordinate di due punti del piano cartesiano calcolare la loro distanza.
qg Date le coordinate di due punti del piano cartesiano determinare i coefficienti della retta passante per
essi (controllare che la retta non sia verticale).
qh Dati il nome e l’età di due persone indicare prima il nome del più giovane e dopo quello del più vecchio.
ql Scrivere un programma che legge tre numeri in virgola mobile e che sceglie e scrive il maggiore dei tre.
111
Unitˆ didattica
7
Istruzioni di selezione
$ Sintassi dellÕistruzione if
$ Come organizzare if nidificati
$ Sintassi dellÕistruzione switch
Unitˆ didattica
Istruzioni di selezione
7
I programmi scritti finora per illustrare in pratica i concetti che sono stati sviluppati non erano dota-
ti di particolari strutture che consentissero un minimo controllo sul flusso del programma. Sono sta-
ti trattati anche tutti gli operatori relazionali e logici, ed • ora il momento di utilizzarli in contesti in
cui poter apprezzare le loro effettive qualitˆ nella gestione del flusso del codice.
7.1 Istruzione if
Gli operatori relazionali assumono la loro importanza dal fatto che consentono di prendere decisio-
ni in base al loro risultato.
ÒPrendere una decisioneÓ significa essere in grado di eseguire un blocco di codice al verificarsi di una
determinata condizione. C++, come tutti i linguaggi di alto livello, mette a disposizione lÕistruzione
if che consente al programma di eseguire un blocco di codice solo in base al verificarsi di una de-
terminata condizione.
La sua sintassi •:
if (condizione)
{
//blocco di codice
}
dove condizione • un test in cui vengono messi in relazione due operandi tramite un operatore
relazionale o logico. LÕistruzione if, con la sintassi vista sopra, realizza quanto visto in pseudocodi-
fica per la struttura di selezione a una via.
SE condizione
ALLORA
blocco di istruzioni
FINE SE
Quindi le istruzioni, allÕinterno del blocco, vengono eseguite solo se la condizione risulta verificata
(true).
Pseudocodifica
INIZIO
leggi (numero)
SE numero > 50
ALLORA
scrivi (Òil numero • maggiore di 50Ó)
FINE SE
FINE
113
Sezione 3 - Organizzazione degli algoritmi
Codice
If1.cpp
1 include <iostream>
2 using namespace std;
3 //INIZIO
4 int main ()
5 {
6 //dichiara la variabile intera
7 int numero;
8
9 //chiedi numero
10 cout << "\nInserisci un numero ";
11
12 //leggi un numero
13 cin >> numero;
14
15 //controlla se • > di 50
16 if (numero>50)
17 {
18 //scrivi se la condizione • verificata (true)
19 cout << "\nIl numero • maggiore di 50";
20 }
21
22 //salta due righe
23 cout << endl << endl;
24 //arresta l'esecuzione del programma
25 system ("pause");
26 //termina il programma
27 return 0;
28 }
Prova di esecuzione
Il codice del programma precedente informa lÕutente solo al verificarsi della condizione; in caso con-
trario, non emette alcun messaggio.
114
Unitˆ didattica 7 - Istruzioni di selezione
Pseudocodifica
INIZIO
leggi (numero)
SE numero > 50
ALLORA
scrivi (ÒIl numero • maggiore di 50Ó)
ALTRIMENTI
scrivi (ÒIl numero non • maggiore di 50Ó)
FINE SE
FINE
Codice
If_Else.cpp
1 #include <iostream>
2 using namespace std;
3 //INIZIO
4 int main ()
115
Sezione 3 - Organizzazione degli algoritmi
5 {
6 //dichiara una variabile intera
7 int numero;
8
9 //chiedi numero
10 cout << "\nInserisci un numero ";
11
12 //leggi un numero
13 cin >> numero;
14
15 //controlla se • > di 50
16 if (numero>50)
17 {
18 //scrivi che la condizione • verificata (true)
19 cout << "\nIl numero • maggiore di 50";
20 }
21 else
22 {
23 //scrivi che la condizione non • verificata (false)
24 cout << "\nIl numero non • maggiore di 50";
25 }
26
27 //salta due righe
28 cout << endl << endl;
29 //arresta l'esecuzione del programma
30 system ("pause");
31 //termina il programma
32 return 0;
33 }
Prova di esecuzione
La condizione che segue la parola if pu˜ essere semplice, come negli esempi precedenti, oppure
pu˜ essere una condizione composta da pi• enunciati legati tra loro dai connettivi logici And, Or,
Xor e Not.
Esempio If_And...................................................................................................................
Verificare che un numero letto da tastiera sia compreso nellÕintervallo che va da 10 a 100.
Il numero • compreso tra 10 e 100 quando risulta maggiore o uguale a 10 e minore o ugua-
le a 100.
116
Unitˆ didattica 7 - Istruzioni di selezione
Pseudocodifica
INIZIO
leggi (numero)
SE numero >= 10 AND numero <= 100
ALLORA
scrivi (ÒIl numero • compreso nellÕintervalloÓ)
ALTRIMENTI
scrivi (ÒIl numero • esterno allÕintervalloÓ)
FINE SE
FINE
Codice
If_And.cpp
1 #include <iostream>
2 using namespace std;
3 //INIZIO
4 int main ()
5 {
6 //dichiara una variabile intera
7 int numero;
8
9 //chiedi numero
10 cout << "\nInserisci un numero ";
11
12 //leggi un numero
13 cin >> numero;
14
15 //controlla se • compreso nell'intervallo
16 if ((numero>=10)&&(numero<=100))
17 {
18 //scrivi che la condizione • verificata (true)
19 cout << "\nIl numero • compreso";
20 }
21 else
22 {
23 //scrivi che la condizione non • verificata (false)
24 cout << "\nIl numero non • compreso";
25 }
26
27 //salta due righe
28 cout << endl << endl;
29 //arresta l'esecuzione del programma
30 system ("pause");
31 //termina il programma
32 return 0;
33 }
Prova di esecuzione
117
Sezione 3 - Organizzazione degli algoritmi
7.3 If nidificati
Riprendiamo lÕesempio ÒIf_ElseÓ relativo allÕutilizzo della struttura di alternativa.
Si vuole verificare che un numero intero inserito da tastiera sia uguale a 50. Una possibile soluzione
pu˜ essere quella di inserire unÕaltra istruzione if..else nel blocco di codice relativo allÕelse.
Infatti, una volta stabilito che il numero inserito risulta Ònon maggiore di 50Ó, si hanno due pos-
sibilitˆ:
1. il numero inserito • minore di 50;
2. il numero inserito • uguale a 50.
Codice
Else_If.cpp
1 #include <iostream>
2 using namespace std;
3 //INIZIO
4 int main ()
5 {
6 //dichiara la variabile intera
7 int numero;
8
9 //chiedi numero
10 cout << "\nInserisci un numero ";
11
12 //leggi un numero
13 cin >> numero;
14
15 //controlla numero
16 if (numero>50)
17 {
18 //• verificata la prima possibilitˆ
19 cout << "\nIl numero inserito • pi• grande di 50";
20 }
21 else if (numero<50) //2a possibilitˆ
22 {
23 //• verificata la seconda possibilitˆ
24 cout << "\nIl numero inserito • pi• piccolo di 50";
25 }
26 else //3a possibilitˆ
118
Unitˆ didattica 7 - Istruzioni di selezione
27 {
28 //• verificata la terza possibilitˆ
29 cout << "\nIl numero inserito • uguale a 50";
30 }
31
32 //salta due righe
33 cout << endl << endl;
34 //arresta l'esecuzione del programma
35 system ("pause");
36 //termina il programma
37 return 0;
38 }
Prova di esecuzione
Si noti lÕuso di parentesi graffe dopo il costrutto else di riga 21: queste servono a racchiudere un bloc-
co di codice composto da due o pi• istruzioni.
In questo esempio non • necessario inserirle (anche se nessuno lo vieta), in quanto il blocco di codice
relativo • composto da una sola istruzione. Le righe che vanno dalla 16 alla 31 si possono tranquilla-
mente riscrivere come segue.
...
//controlla numero
if (numero>50)
//• verificata la prima possibilitˆ
cout << "\nIl numero inserito • pi• grande di 50";
else if (numero<50) //2a possibilitˆ
//• verificata la seconda possibilitˆ
cout << "\nIl numero inserito • pi• piccolo di 50";
else //3a possibilitˆ
//• verificata la terza possibilitˆ
cout << "\nIl numero inserito • uguale a 50";
...
...........................................................................................................................................
119
Sezione 3 - Organizzazione degli algoritmi
: se il valore delle variabili è presente in una delle liste di valori viene eseguito il blocco di istru-
zioni corrispondente, fino alla prima istruzione ESCI; successivamente si procede con l’esecu-
zione della prima istruzione che segue la riga FINE SCEGLI;
: in caso contrario viene eseguito il blocco di istruzioni indicato dopo la riga ALTRIMENTI.
Se, per esempio, si deve associare a un numero compreso tra 1 e 7 il relativo giorno della settimana,
utilizzando l’istruzione if dobbiamo utilizzare sette costrutti if..else, controllando sempre che
il numero inserito non sia esterno ai limiti numerici imposti.
Nel linguaggio C++ la scelta multipla è realizzata dall’istruzione switch, che ha la sintassi riporta-
ta di seguito.
switch (variabile_di_controllo)
{
case valori1:
istruzioni1;
break;
case valori2:
istruzioni2;
break;
...
case valorin:
istruzionin;
break;
default:
istruzioni;
}
120
Unitˆ didattica 7 - Istruzioni di selezione
Dopo la parola chiave switch viene indicato il nome della variabile di controllo (detta anche va-
riabile selettore), di cui si deve controllare il valore per decidere quale strada seguire tra quelle pos-
sibili. Accanto ai valori previsti • consigliabile non dimenticare di scrivere le istruzioni da eseguire nel
caso in cui la variabile non assuma nemmeno uno dei valori indicati, inserendo la parola chiave de-
fault; se tale etichetta non • presente e non vi • stata alcuna corrispondenza tra la variabile di con-
trollo e le etichette case il controllo passa alla prima istruzione che segue il costrutto switch.
é bene inserire sempre la clausola default nel costrutto switch in quanto, grazie a essa, si posso-
no indicare istruzioni particolari nel caso in cui non esista alcuna corrispondenza tra la variabile di
controllo e i valori presenti nei case.
Viene generato un errore di compilazione se due o pi• etichette case fanno riferimento al medesi-
mo insieme di valori.
Per meglio chiarire la funzionalitˆ del costrutto switch codifichiamo lÕesempio della corrisponden-
za tra un numero inserito da tastiera e il relativo giorno della settimana.
Codice
Settimana.cpp
1 #include <iostream>
2 using namespace std;
3 //INIZIO
4 int main ()
5 {
6 //dichiara una variabile intera
7 int numero;
8
9 //chiedi un numero
10 cout << "\nInserisci un numero intero (da 1 a 7): ";
11
12 //leggi numero
13 cin >> numero;
14
15 switch (numero)
16 {
17 case 1: cout <<"\nE' Luned“ "; break;
18 case 2: cout <<"\nE' Marted“ "; break;
19 case 3: cout <<"\nE' Mercoled“ "; break;
20 case 4: cout <<"\nE' Gioved“ "; break;
21 case 5: cout <<"\nE' Venerd“ "; break;
22 case 6: cout <<"\nE' Sabato"; break;
23 case 7: cout <<"\nE' Domenica"; break;
24 default: cout <<"\nNumero non corrispondente!!!"; break;
25 }
26
27 //salta due righe
28 cout << endl << endl;
29 //arresta l'esecuzione del programma
30 system ("pause");
31 //termina il programma
32 return 0;
33 }
121
Sezione 3 - Organizzazione degli algoritmi
Prova di esecuzione
case valori4:
case valori5:
istruzioni5;
break;
...
...
default:
istruzioni;
break;
}
I valori dei due primi case, pur essendo differenti, sono stati raggruppati: ci˜ significa che, al verifi-
carsi della corrispondenza di uno dei due valori con quello della variabile variabile_di_con-
trollo, viene eseguito il medesimo codice.
Lo stesso concetto vale per i case 4 e 5.
122
Unitˆ didattica 7 - Istruzioni di selezione
Codice
PariDispari.cpp
1 #include <iostream>
2 using namespace std;
3 //INIZIO
4 int main ()
5 {
6
7 //dichiara una variabile intera
8 int numero;
9
10 //chiedi un numero
11 cout << "\nInserisci un numero intero (da 1 a 10): ";
12
13 //leggi numero
14 cin >> numero;
15
16 switch (numero)
17 {
18 //case che individuano i numeri dispari
19 case 1:
20 case 3:
21 case 5:
22 case 7:
23 case 9: cout <<"\nNumero inserito dispari\n";
24 break;
25
26 //case che individuano i numeri pari
27 case 2:
28 case 4:
29 case 6:
30 case 8:
31 case 10: cout << "\nNumero inserito pari\n";
32 break;
33
34 default: cout << "\nNumero esterno ai limiti!!!\n";
35 break;
36 }
37
38 //salta due righe
39 cout << endl << endl;
40 //arresta l'esecuzione del programma
41 system ("pause");
42 //termina il programma
43 return 0;
44 }
Prova di esecuzione
123
Sezione 3 - Organizzazione degli algoritmi
La variabile di controllo dellÕistruzione switch non pu˜ essere di tipo qualsiasi: sono gestiti tutti i tipi in-
teri, il tipo carattere e gli enumeratori (che vedremo pi• avanti).
Si ricorda che i tipi interi sono int, long e char, mentre una stringa • una sequenza di caratteri rac-
chiusa tra virgolette.
Se il tipo della variabile di controllo • diverso dai valori dei case si rende necessario un suo casting espli-
cito, allo scopo di consentirne la corrispondenza.
...........................................................................................................................................
124
Unità didattica 7 - Istruzioni di selezione
Esercizi
Unità didattica 7
4 Si vuole calcolare l’imponibile IVA e il totale di un importo inserito da tastiera. Se l’importo risulta minore
o uguale a euro 150.00 l’IVA deve essere del 16%; se l’importo è superiore, l’IVA deve essere del 20%.
5 Scrivere il codice C++ per risolvere il problema seguente. In un piano cartesiano si conoscono le coor-
dinate di un vertice e la lunghezza del lato di un quadrato. Si consideri che i lati del quadrato sono pa-
ralleli agli assi e che il vertice di cui si conoscono le coordinate è quello in basso a sinistra.
6 Scrivere il codice C++ per risolvere il problema seguente: dati i coefficienti di un'equazione di secondo
grado calcolare, se esistono, le due soluzioni reali.
9 Perché bisogna inserire la parola chiave break alla fine dei case?
qs Scrivere un programma che associ a ogni mese dell’anno, inserito da tastiera e indicato da un numero
intero da 1 a 12, il nome corrispondente.
qd Scrivere un programma che notifichi a quale dei quattro trimestri appartiene un mese inserito da ta-
stiera.
125
Sezione 3 - Organizzazione degli algoritmi
Esercizi
Unità didattica 7
qf Date le coordinate di due punti sul piano cartesiano, determina il coefficiente angolare della retta pas-
sante per essi (controlla che la retta non sia verticale).
qh Dato in input un numero intero compreso tra 0 e 15, scrivi la sua rappresentazione esadecimale.
qj Dato in input un numero intero compreso tra 0 e 255, scrivi la sua rappresentazione esadecimale (sug-
gerimento: dividere il numero dato per 16 due volte: i due resti, presi in ordine inverso, determinano la
sequenza delle cifre esadecimali).
ql Dati in input tre numeri, indica il valore compreso tra il minimo e il massimo.
w0 Scrivere il codice C++ per risolvere il problema seguente: dati i coefficienti di un'equazione di secondo
grado calcolare, se esistono, le due soluzioni reali.
126
Unitˆ didattica
8
Istruzioni di ripetizione
$ Istruzione while
$ Istruzione do..while
$ Istruzione for
$ Modalitˆ di interruzione
Unità didattica
Istruzioni di ripetizione
8
Nell’Unità didattica precedente sono state studiate tutte quelle istruzioni che, in qualche modo, pos-
sono modificare il flusso di programma. Spesso, però, un programmatore ha la necessità di ripetere un
blocco di codice più volte.
All’interno di una applicazione le istruzioni che consentono di ripetere un blocco di codice vengono
chiamate istruzioni di ripetizione o iterative o, più grossolanamente, cicli.
Queste istruzioni, insieme alle strutture di controllo, rivestono un’enorme importanza nella pro-
grammazione, quale che sia il linguaggio di codifica.
: while;
: do..while;
: for.
while (condizione)
{
blocco_di_istruzioni;
}
L’istruzione while valuta la condizione tra parentesi, chiamata condizione di controllo e, se risulta
verificata (true), vengono eseguite le istruzioni all’interno delle parentesi graffe. Una volta esegui-
te tutte le istruzioni, la condizione di controllo viene nuovamente valutata e, solo se risulta non ve-
rificata, il flusso del programma prosegue con l’istruzione successiva al blocco while, altrimenti
vengono nuovamente eseguite le istruzioni al suo interno e così via.
L’istruzione while realizza il costrutto in pseudocofica visto per la struttura di ripetizione precon-
dizionale, che ha la forma seguente.
MENTRE condizione
blocco di istruzioni
FINE MENTRE
Facciamo un esempio.
128
Unitˆ didattica 8 - Istruzioni di ripetizione
Pseudocodifica
//input
num
//output
num
//variabile di lavoro
i
INIZIO
i ←1
leggi (num)
MENTRE i<= 10
scrivi (i, num, num * i)
i←i+1
FINE MENTRE
FINE
Codice
Tabellina1.cpp
1 #include <iostream>
2 using namespace std;
3 //INIZIO
4 int main ()
5 {
6 //definisci il contatore i
7 int i=1;
8
9 //dichiara una variabile intera. chiedi e leggi un numero
10 int num;
11 cout << "\nInserisci un numero intero (da 1 a 10): ";
12 cin >> num;
13
14 //ciclo while
15 while(i<=10)
16 {
17 cout <<"\n" << " " << i << " " << num << " " << num*i;
18 //incrementa il contatore
19 ++i;
20 }
21
22 //salta due righe
23 cout << endl << endl;
24 //arresta l'esecuzione del programma
25 system ("pause");
26 //termina il programma
27 return 0;
28 }
129
Sezione 3 - Organizzazione degli algoritmi
Prova di esecuzione
8.2 Tabulazione
Il programma visto nel paragrafo precedente produce risultati corretti, ma non li presenta in modo
leggibile e ordinato: i numeri disposti in colonna sono disallineati, mentre dovrebbero essere inco-
lonnati a destra, inoltre le colonne dei risultati sono prive di unÕintestazione che chiarisca il significa-
to dei numeri presentati in output.
Per una prima soluzione del problema indicato si deve ricorrere agli strumenti di formattazione visti
nel paragrafo 4.2, Ò Output formattatoÓ. NellÕesempio che segue useremo il metodo setw(n), che
imposta il numero di caratteri occupati dal risultato, anche detto ÒampiezzaÓ.
Tabellina2.cpp
1 #include <iostream>
2 #include "iomanip"
130
Unitˆ didattica 8 - Istruzioni di ripetizione
Prova di esecuzione
131
Sezione 3 - Organizzazione degli algoritmi
to deve essere sempre richiamato il metodo setw() e non • possibile distribuirne lÕeffetto su
pi• dati. Nel nostro esempio per la visualizzazione del contatore vengono riservati 6 caratteri,
per quella degli altri due dati 8 caratteri.
3. Alla riga 16 la medesima operazione di impostazione dellÕampiezza di scrittura viene eseguita
sulle stringhe di intestazione delle colonne, in modo tale che sia i dati numerici della tabella,
sia le intestazioni risultino tutti incolonnati sulla destra.
Si noti che la riga che contiene le intestazioni deve essere scritta una volta sola ed •, pertanto, posta al-
lÕesterno del ciclo while; al contrario, le istruzioni di scrittura a video dei risultati devono essere ripetu-
te pi• volte, quindi sono poste allÕinterno del blocco di istruzioni che • controllato dal ciclo while.
...........................................................................................................................................
Il ciclo do..while garantisce almeno una iterazione; ci˜ si evince dal fatto che la condizione di con-
trollo • posta alla fine di tutte le istruzioni, dopo la parola chiave do. Infatti, prima vengono esegui-
te le istruzioni contenute nel blocco di codice, poi viene eseguito il test di controllo e, se risulta ve-
rificato, il ciclo si ripete, altrimenti si esce dal ciclo proseguendo con lÕistruzione immediatamente
successiva. In realtˆ, tale caratteristica non sempre risulta soddisfacente perchŽ, di solito, le iterazio-
ni devono essere eseguite solo nel caso in cui risulti immediatamente verificata la condizione. Nel ci-
clo do..while, le istruzioni vengono eseguite sempre almeno una volta, a prescindere dal risulta-
to del test di controllo.
Il costrutto do..while realizza la struttura che in pseudocodifica viene chiamata ripetizione post-
condizionale, che • rappresentata dallo schema seguente.
RIPETI
blocco di istruzioni
MENTRE condizione
132
Unitˆ didattica 8 - Istruzioni di ripetizione
Pseudocodifica
INIZIO
//input
voto;
risposta;
//output
media;
//variabili di lavoro
i=0 //contatore i
somma ← 0 //totalizzatore
RIPETI
chiedi e leggi un voto
somma = somma + voto //totalizza i voti
i=i+1 //incrementa contatore
chiedi se elenco terminato e leggi risposta
MENTRE risposta = ÔnÕ
media = somma / i; //calcola media
scrivi media
FINE
Codice
CalcolaMedia.cpp
1 #include <iostream>
2 using namespace std;
3 //INIZIO
4 int main ()
5 {
6 //INPUT
7 int voto;
8 char risposta;
9
10 //OUTPUT
11 double media;
12
13 //variabili di lavoro
14 int i=0; //contatore i
15 double somma=0; //totalizzatore
16
17 do
18 {
19 //chiedi e leggi un voto
20 cout << "\nInserisci un voto (da 1 a 10): ";
21 cin >> voto;
22
23 //totalizza i voti
24 somma += voto;
25
26 //incrementa contatore
27 i++;
28
29 //chiedi se elenco terminato e leggi risposta
30 cout << "\nl'elenco dei voti • terminato (s/n) ";
133
Sezione 3 - Organizzazione degli algoritmi
Prova di esecuzione
134
Unitˆ didattica 8 - Istruzioni di ripetizione
indicate dopo la parola for e sulla riga di for viene indicato il criterio per contare il numero di ri-
petizioni. Diversamente dal costrutto while, nel ciclo di tipo for la variabile contatore viene in-
crementata o decrementata a ogni ripetizione delle istruzioni al suo interno.
In pseudocodifica la ripetizione enumerativa si indica con le parole indicate di seguito.
PER contatore DA inizio A fine [con PASSO incremento]
istruzioni
FINE PER
Tale struttura richiede che la variabile ÒcontatoreÓ venga impostata al valore di inizio e fa in modo
che, ogni volta che vengono eseguite le istruzioni comprese tra PER e FINE PER, la variabile Òcon-
tatoreÓ venga incrementata di un valore uguale a ÒincrementoÓ.
La parte dellÕistruzione compresa tra parentesi quadre pu˜ essere omessa: in tal caso lÕincremento •
da considerarsi uguale a 1.
Nella maggior parte dei casi, la struttura PER si presenta in una forma semplificata rispetto a quella
vista: per esempio, se si vuole ripetere un gruppo di istruzioni per 10 volte, basta scrivere:
PER contatore DA 1 A 10
istruzioni
RIPETI
Esaminando il ciclo while del programma di esempio tabellina.cpp (e in particolare il codice
relativo al ciclo while), si possono riscontrare alcuni elementi di base che ci portano alla definizio-
ne del ciclo for.
//definisci il contatore i
int i=1;
....
//ciclo while
while(i<=10)
{
cout <<"\n" << " " << i << " " << num << " " << num*i;
//incrementa il contatore
++i;
}
Il C++, come tutti i linguaggi di alto livello, mette a disposizione unÕistruzione che riunisce tre dei
quattro punti precedenti in un unico costrutto: il ciclo for.
La sua sintassi • riportata di seguito.
for (inizializzazione; controllo; incremento)
{
blocco di istruzioni
}
In cui ogni espressione che fa parte della condizione, che viene indicata tra parentesi tonde, deve es-
sere separata obbligatoriamente da un punto e virgola.
135
Sezione 3 - Organizzazione degli algoritmi
Si ha che:
Volendo quindi riscrivere il ciclo utilizzando l’istruzione for, il ciclo while precedente assume la
forma seguente:
L’istruzione for viene utilizzata per ripetere un blocco di codice per un numero stabi-
lito di volte.
In primo luogo esegue l’espressione di inizializzazione del contatore e lo fa, ovviamente, solo la
prima volta; poi controlla la condizione e, se questa risulta verificata, vengono elaborate tutte le
istruzioni del blocco di codice interno, altrimenti vengono eseguite le istruzioni successive al ci-
clo. Se la condizione di controllo risulta verificata, dopo che le istruzioni interne al ciclo sono sta-
te eseguite il flusso ritorna all’istruzione for, che incrementa il contatore ed esegue nuovamente
il test di controllo. Tutto il processo si ripete fino a che la condizione di controllo non risulta più
verificata.
Si tratta di moltiplicare la sequenza degli interi che vanno da 1 a un numero intero inserito da tastie-
ra. Un simile calcolo viene effettuato mediante un ciclo che ripete la stessa operazione, che consiste
nell’esecuzione di una moltiplicazione tante volte fino ad arrivare al numero inserito. In matematica,
questo genere di calcolo prende il nome di fattoriale. Per esempio, il fattoriale del numero 5 è 120
e si indica con 5!; il calcolo da eseguire è 1 * 2 * 3 * 4 * 5 = 120.
Il calcolo del fattoriale viene effettuato moltiplicando tra loro il numero dato e tutti i numeri prece-
dentemente forniti. Per esempio: il fattoriale di 6 (si scrive 6!) è 720 cioè, 1 * 2 * 3 * 4 * 5 * 6.
Pseudocodifica
//input
num
//output
fatt
//variabili di lavoro
i
INIZIO
136
Unitˆ didattica 8 - Istruzioni di ripetizione
fatt ← 1
scrivi (Ò\nInserisci un numero intero (Max 10) = Ó)
leggi (num)
PER i DA 1 A num
fatt ← fatt * i
FINE PER
scrivi (fatt)
FINE
Codice
Fattoriale.cpp
1 #include <iostream>
2 using namespace std;
3 //INIZIO
4 int main ()
5 {
6 //input
7 int num;
8 //output
9 int fatt;
10
11 //chiedi e leggi il numero su cui calcolare il fattoriale
12 cout << "\nInserisci un numero intero (Max 10) = ";
13 cin >> num;
14
15 //inizializzazione
16 fatt = 1;
17 //esegue il ciclo for
18 for (int i=1; i<=num; i++)
19 {
20 fatt *= i;
21 }
22
23 //scrivi il risultato
24 cout << "\nFattoriale = " << fatt;
25
26 //salta due righe
27 cout << endl << endl;
28 //arresta l'esecuzione del programma
29 system ("pause");
30 //termina il programma
31 return 0;
32 }
Prova di esecuzione
137
Sezione 3 - Organizzazione degli algoritmi
A questo punto ci si potrebbe chiedere: ÒPerchŽ usare un ciclo for piuttosto che un while?Ó. Le istru-
zioni iterative while, do e for consentono di creare cicli ciascuno con caratteristiche strutturali diffe-
renti, che si adeguano a situazioni specifiche.
Quando in un programma si deve utilizzare unÕistruzione di ripetizione e si • a conoscenza dellÕesatto nu-
mero di cicli da effettuare, come nellÕesempio appena visto, sarebbe meglio utilizzare un ciclo for; se,
invece, il numero di iterazioni dipende da un valore che il programmatore non pu˜ stabilire a priori, come
nel caso del programma CalcolaMedia.cpp (in cui la condizione di terminazione del ciclo poteva es-
sere verificata anche dopo migliaia di inserimenti di voti), si dovrebbe usare un ciclo while; infine, se il
blocco di codice da iterare deve essere eseguito almeno una volta, • conveniente utilizzare il ciclo
do..while.
Per concludere, possiamo dire che la scelta del tipo di ciclo da eseguire • strettamente correlata alla na-
tura del problema e allÕalgoritmo utilizzato per risolverlo.
...........................................................................................................................................
138
Unità didattica 8 - Istruzioni di ripetizione
Esercizi
Unità didattica 8
1 Dato il codice:
int i = 0;
do
i++;
while (i >= 5)
cout << i;
2 Dato il codice:
int i = 0;
while (i >= 5)
{
i++;
}
cout << i;
3 Dato il codice:
int i = 0;
do
i++;
while (i < 5)
cout << i;
4 Dato il codice:
int i = 0;
while (i < 5)
{
i++;
}
cout << i;
5 Dato il codice:
int i = 0;
do
i++;
cout << i;
while (i >= 5)
139
Sezione 3 - Organizzazione degli algoritmi
Esercizi
Unità didattica 8
6 Dato il codice:
int i = 0;
while (i >= 5)
{
i++;
cout << i;
}
7 Dato il codice:
int i = 0;
do
i++;
cout << i;
while (i < 5)
8 Dato il codice:
int i = 0;
while (i < 5)
{
i++;
cout << i;
}
9 Dato un numero intero N, calcolare la somma dei quadrati degli interi minori o uguali a N.
qs Determinare se un numero intero è primo (un numero è primo se nessun numero minore o uguale alla
sua metà è divisore del numero dato (N è divisore di M, se M % N = 0).
qd Dato l'elenco dei voti di uno studente, calcolare la media dei voti.
qf Dati in input il valore di un deposito bancario e il tasso di interesse annuo, calcolare gli interessi matu-
rati dopo 5 anni.
140
Unità didattica 8 - Istruzioni di ripetizione
Esercizi
Unità didattica 8
qj Dato l'elenco dei voti di uno studente, calcolare la media dei voti. Si devono ripetere le seguenti istru-
zioni:
leggi voto
somma ← somma + numero
Chiedi “elenco finito ?”
leggi risposta
qk Data in input una serie di numeri, contare quelli pari e quelli dispari; comunicare anche la percentuale
sul totale dei pari e dei dispari. Si devono ripetere le seguenti istruzioni:
leggi numero
SE numero è pari
ALLORA conta pari
ALTRIMENTI conta dispari
FINE SE
Chiedi “serie finita ?”
leggi risposta
w0 È dato in input un elenco di N studenti (con N dato in input): per ogni studente si conoscono il nome, il
sesso e il voto in informatica. Calcolare la media dei voti di tutti gli studenti e la media dei voti dei ma-
schi e delle femmine.
wa Con i dati di input dell’esercizio precedente scrivere il nome dello studente che ha il voto più alto tra gli
studenti maschi, e il nome della studentessa che ha il voto più alto tra le studentesse.
141
Unitˆ didattica
9
Le funzioni
: tipo_restituito: specifica il tipo di dato restituito dalla funzione. Una funzione può re-
stituire un qualsiasi tipo di dato, tranne un array.
: elenco_parametri: è un elenco di nomi di variabili separati da virgole e preceduti dai ri-
spettivi tipi; tali variabili sono destinate a ricevere i valori degli argomenti nel momento in cui
la funzione viene richiamata. Una funzione può anche non avere parametri, nel qual caso l’e-
lenco dei parametri sarà vuoto; tuttavia, anche in mancanza di parametri, è richiesta la presen-
za delle parentesi.
Nelle istruzioni di dichiarazione delle variabili è possibile dichiarare più variabili facenti capo a uno
stesso tipo, inserendo l’elenco dei nomi di variabili separati da virgole; al contrario, tutti i parametri
di una funzione devono essere dichiarati singolarmente e di ognuno si deve specificare sia il tipo sia
il nome: questo significa che la dichiarazione dei parametri di una funzione ha la seguente forma ge-
nerale:
Nelle Unità didattiche precedenti è stata più volte incontrata la parola funzione, che è stato detto esse-
re una porzione di codice che viene richiamata tramite il suo nome.
Nonostante il C++ metta a disposizione tutte le funzionalità ad hoc per i nostri programmi, spesso si
ha la necessità, data la natura specifica di un’applicazione, di dover scrivere nuove funzioni che più si
adattano alle nostre esigenze.
Il linguaggio non mette a disposizione certo, per esempio, una funzione che calcola il perimetro di
un trapezio, ma, tramite l’uso delle istruzioni di calcolo, si può scrivere una funzione che effettui tut-
te le operazioni.
Dopo aver visto i concetti principali della programmazione, si può passare a esaminare meglio che
cos’è una funzione.
143
Sezione 3 - Organizzazione degli algoritmi
Le funzioni sono fondamentali per due motivi: innanzitutto, suddividono il programma in parti pi•
ridotte e lo rendono pi• comprensibile; in secondo luogo, permettono il riutilizzo di segmenti di co-
dice in diversi programmi. Come sapete, quando si scrive il codice, si parte da un algoritmo di alto li-
vello e si continua a rifinirlo nel dettaglio finchŽ non si ottiene il codice di un programma che espri-
ma tutti gli algoritmi fino a comprendere quello di alto livello. Una funzione descrive una ÒrigaÓ in
uno di questi algoritmi, per esempio Òapri un fileÓ, Òvisualizza il testo sullo schermoÓ, Òstampa un
documentoÓ e cos“ via.
Inoltre, una funzione pu˜ essere richiamata, allÕinterno di un programma, tutte le volte che • neces-
sario senza ripetere ogni volta le istruzioni in essa contenute. Quindi, una volta scritta e definita una
funzione, il codice al suo interno viene eseguito ogni volta che il flusso del programma incontra una
sua invocazione.
Definire una funzione significa creare e dotare di una nuova funzionalitˆ il programma che si sta scri-
vendo. La sua definizione assume la seguente forma generale.
intestazione funzione
{
blocco_di_codice_della_funzione
}
Ciao.cpp
1 #include "iostream"
2 using namespace std;
3 //INIZIO
4 int main ()
5 {
6 //scrivi il messaggio di saluto
7 cout << "Ciao, mondo!" << endl;
8 //arresta l'esecuzione del programma
9 system ("pause");
10 //termina il programma
11 return 0;
12 }
Alla riga 4 cÕ• la dichiarazione del ÒcontenitoreÓ del nostro codice. Tale contenitore • una funzione
e si chiama main. Il codice del programma • delimitato dalle parentesi graffe di riga 5 e riga 12.
int main()
{
//scrivi il messaggio di saluto
cout << "Ciao, mondo!" << endl;
...
144
Unità didattica 9 - Le funzioni
la funzione main() è una funzione speciale che ogni applicazione deve incorporare, ma se si vo-
gliono suddividere logicamente le funzionalità del programma occorre scrivere nuove funzioni, da
richiamarsi per eseguire i vari compiti.
Si può ora modificare il programma Ciao.cpp, inserendo una nuova funzione che effettua la scrit-
tura del messaggio di saluto.
void saluto()
{
//scrivi il messaggio di saluto
cout << "Ciao, mondo!" << endl;
}
La funzione si chiama saluto, non restituisce alcun valore e non ha parametri. Poiché, in questo
esempio, la funzione esegue una elaborazione, ma non restituisce alcun valore, il tipo di dato asso-
ciato alla funzione è void. Il corpo è composto da una sola semplice istruzione di stampa.
Quando una funzione non restituisce alcun valore viene anche chiamata procedura
e il valore restituito mancante è specificato mediante la parola chiave vod.
Codice
Ciao1.cpp
1 #include "iostream"
2 using namespace std;
3
4 void saluto()
5 {
6 //scrivi il messaggio di saluto
7 cout << "Ciao, mondo!" << endl;
8 }
9
10 //INIZIO
11 int main ()
12 {
145
Sezione 3 - Organizzazione degli algoritmi
13 saluto();
14
15 //arresta l'esecuzione del programma
16 system ("pause");
17 //termina il programma
18 return 0;
19 }
main
//INIZIO
int main()
{
Invocazione
saluto();
funzione
//arresta lÕesecuzione del programma
system(ÓpauseÓ);
//termina il programma
return 0;
}
funzione saluto()
void saluto()
{
//scrivi il messaggio di saluto
cout << ÒCiao, mondo!Ó << endl;
}
Ritorna
al main
...........................................................................................................................................
146
Unitˆ didattica 9 - Le funzioni
Si pu˜ capire, dalla breve analisi effettuata, che il programma deve assolvere a tre ÒcompitiÓ: lettura
dei dati, calcolo del risultato e stampa del risultato. Questi lavori si prestano benissimo a essere rea-
lizzati con altrettante funzioni cui daremo, rispettivamente, i seguenti nomi:
1. leggiLato();
2. calcolaQuadr();
3. scriviQuadr().
Codice
Quadrato1.cpp
1 #include <iostream>
2 using namespace std;
3
4 void leggiLato()
5 {
6 //chiedi e leggi lato
7 cout << "\nInserisci la misura del lato ";
8 cin >>lato;
9 }
10
11 void calcolaQuadr()
12 {
13 //calcola area quadrato
14 area = lato * lato;
15 }
16
17 void scriviQuadr()
18 {
19 //scrivi area
20 cout << "\nArea quadrato = " << area;
21 }
147
Sezione 3 - Organizzazione degli algoritmi
22
23 //INIZIO
24 int main ()
25 {
26 //dichiara variabili lato e area
27 double lato;
28 double area;
29
30 //invoca le funzioni
31 leggiLato();
32 calcolaQuadr();
33 scriviQuadr();
34
35 //fine programma
36 cout << "\n\nFine ";
37 system ("pause");
38 return 0;
39 }
...........................................................................................................................................
La compilazione del codice indicato sopra, per˜, dˆ luogo a una discreta lista di errori; di seguito ri-
portiamo a titolo di esempio che cosa appare nella finestra che elenca gli errori nel programma di edi-
ting Microsoft Visual C++ 2005 Express Edition.
148
Unitˆ didattica 9 - Le funzioni
automaticamente allÕuscita dalla funzione, cio• viene perso ogni loro riferimento, quindi le variabili
appartenenti alla funzione main() vengono definite variabili locali in quanto sono state dichiarate
e utilizzate nel corpo della funzione stessa. Con lÕinserimento di nuove funzioni nel codice • neces-
sario definire il campo dÕazione (ambito) di ciascuna variabile, per controllare in quali funzioni deve
essere disponibile.
Gli errori elencati nel tentativo di compilazione del programma Quadrato.cpp spiegano che il
contesto in cui operano le variabili dichiarate • errato: infatti, nel codice tutte le variabili utilizzate
nel programma sono state dichiarate allÕinterno della funzione main(), quindi sono locali a essa e
in quanto tali possono essere utilizzate solo ed esclusivamente in tale ambito.
Le altre funzioni presenti nel codice non possono essere a conoscenza della loro presenza, quindi
richiamandone i nomi fanno riferimento a entitˆ che si trovano al di fuori del loro ambito operati-
vo: ecco il motivo degli errori.
A questo punto si rende necessario cambiare lÕambito delle variabili utilizzate dalle nuove funzio-
ni, dotandole di un livello di visibilitˆ pi• ampio: si rende necessario dar loro visibilitˆ globale, af-
finchŽ anche le altre funzioni oltre a main possano utilizzarle. Per questo vengono dette variabi-
li globali.
Le variabili globali vengono definite allÕinterno del contenitore del codice, non pi• al-
lÕinterno della funzione main() (o altre funzioni).
La modifica necessaria per evitare gli errori di compilazione visti prima • illustrata nellÕesempio che
segue.
Codice
Quadrato_corretto.cpp
1 #include <iostream>
2 using namespace std;
3
4 //dichiara variabili lato e area
5 double lato;
6 double area;
7
8 void leggiLato()
9 {
10 //chiedi e leggi lato
11 cout << "\nInserisci la misura del lato ";
12 cin >>lato;
13 }
14
15 void calcolaQuadr()
16 {
17 //calcola area quadrato
18 area = lato * lato;
19 }
20
149
Sezione 3 - Organizzazione degli algoritmi
21 void scriviQuadr()
22 {
23 //scrivi area
24 cout << "\nArea quadrato = " << area;
25 }
26
27 //INIZIO
28 int main ()
29 {
30 //invoca le funzioni
31 leggiLato();
32 calcolaQuadr();
33 scriviQuadr();
34
35 //fine programma
36 cout << "\n\nFine ";
37 system ("pause");
38 return 0;
39 }
Prova di esecuzione
LÕinserimento delle funzioni allÕinterno del programma non segue alcun ordine particolare: possono es-
sere collocate, per esempio, in ordine inverso rispetto a quello precedentemente presentato, ma le loro
intestazioni devono sempre essere collocate prima della funzione main(). LÕimportante • che vengano
invocate secondo la struttura logica stabilita dal programmatore. Nel blocco di codice di una funzione
possono essere invocate tutte le funzioni esterne che si desidera, ma non possono essere dichiarate al-
tre funzioni, ovvero una funzione non pu˜ contenere unÕaltra funzione.
150
Unitˆ didattica 9 - Le funzioni
Esempio Segno...................................................................................................................
Creare una funzione che cambi il segno a una variabile intera.
Codice
Segno.cpp
1 #include <iostream>
2 using namespace std;
3
4 void cambiaSegno ()
5 {
6 int num; //variabile locale
7 num = -num; //cambia segno alla variabile locale
8 }
9
10 //INIZIO
11 int main ()
12 {
13 int num = 125; //variabile globale
14
15 //invoca la funzione cambia segno
16 cambiaSegno();
17
18 //scrivi num
19 cout << "\nLa variabile num contiene " << num;
20
21 //fine programma
22 cout << "\n\nFine ";
23 system ("pause");
24 return 0;
25 }
Prova di esecuzione
151
Sezione 3 - Organizzazione degli algoritmi
Il C++, pur accertando la presenza di due variabili con lo stesso nome e tipo, ne ammette chiara-
mente l’uso, in quanto fanno parte di due ambiti completamente diversi: la variabile num definita al-
la riga 6 è stata dichiarata all’interno della funzione cambiaSegno(), quindi locale, mentre la va-
riabile num definita alla riga 13 è stata dichiarata come appartenente alla funzione main, quindi glo-
bale rispetto a tale funzione.
Il risultato dell’elaborazione è frutto dell’utilizzo della variabile locale che, in questo esempio, “vie-
ne nascosta” dal campo d’azione della variabile globale.
...........................................................................................................................................
: tipo rappresenta uno dei tipi di dato C++ validi e indica il tipo della variabile che la funzione
restituisce al codice chiamante;
: return è la parola chiave dell’istruzione che restituisce un valore di quel tipo.
152
Unitˆ didattica 9 - Le funzioni
17 //calcola quadrato
18 return lato*lato;
19 }
20
21 void scriviQuadr()
22 {
23 //scrivi area
24 cout << "\nArea quadrato = " << area;
25 }
26
27 //INIZIO
28 int main ()
29 {
30 //invoca le funzioni
31 leggiLato();
32 area = calcolaQuadr();
33 scriviQuadr();
34
35 //fine programma
36 cout << "\n\nFine ";
37 system ("pause");
38 return 0;
39 }
Per alcune funzioni lÕinserimento dellÕistruzione return non • indispensabile: se la funzione deve ese-
guire un compito che non richiede la ÒconsegnaÓ di un risultato al main, per essa non • richiesta lÕistru-
zione return. é il caso della funzione scriviQuadr(), che deve eseguire unÕoperazione di output. In-
vece, le funzioni che alla fine della loro esecuzione producono un risultato devono contenere lÕistruzione
return. Rientra in questa categoria anche la funzione leggiLato.
153
Sezione 3 - Organizzazione degli algoritmi
Le funzioni fin qui utilizzate non prevedevano parametri ma, a volte, si ha la necessitˆ di dover co-
municare loro dei valori (argomenti o parametri) che vengono utilizzati dal codice della funzione
stessa. Come si ricorderˆ, lÕintestazione tipo di una funzione • la seguente:
tipo-di-ritorno nome-funzione([lista parametri])
{
blocco di codice della funzione
}
dove, oltre allÕeventuale tipo di ritorno e al nome della funzione, tra parentesi tonde compare una li-
sta (opzionale) di parametri; tale lista comprende una serie di coppie definite dal tipo e dal nome del-
la variabile, definite localmente alla funzione in questo modo:
tipo-di-ritorno nome-funzione([tipo1 var1, tipo2 var2,...])
{
blocco di codice
}
Ogni coppia • separata da una virgola. LÕinvocazione della funzione, dal canto suo, deve contenere
lÕesatta lista degli argomenti, dello stesso tipo, passati come parametri alla funzione, in questo modo:
nome-funzione (variabile1, variabile2, ...);
Codice
AreaRettangolo.cpp
1 #include <iostream>
2 using namespace std;
3
4 double calcolaArea(double b, double h)
5 {
6 //calcola area rettangolo
7 return b*h;
8 }
9
10 //INIZIO
11 int main ()
12 {
13 double base;
14 double altezza;
15 double area;
16
17 //chiedi e leggi base
18 cout << "\nInserisci misura base ";
19 cin >>base;
20 //chiedi e leggi altezza
21 cout << "\nInserisci misura altezza ";
22 cin >>altezza;
23
24 //invoca le funzioni
154
Unitˆ didattica 9 - Le funzioni
Prova di esecuzione
Lo schema che riassume graficamente il passaggio dei parametri per valore a una
funzione • riportato di seguito.
area = calcolaArea(base, altezza); base,altezza: argomenti della funzione
Gli argomenti passati come parametri alla funzione devono rispettare fedelmente sia il tipo di dato sia
lÕordine di dichiarazione dellÕintestazione della funzione, altrimenti il compilatore genera un errore
perchŽ non riesce a trovare la funzione da eseguire, non essendoci corrispondenza di tipo e di nu-
mero degli argomenti. Inoltre, nellÕintestazione della funzione non • consentito assegnare valori ai pa-
rametri.
Si ricordi che tutti i parametri passati per valore sono considerati locali alla funzione: al termine dellÕela-
borazione, dopo lÕesecuzione dellÕistruzione return, i loro valori non esisteranno pi•.
155
Sezione 3 - Organizzazione degli algoritmi
In altre parole, per ogni argomento viene generata un copia che viene assegnata al corrisponden-
te parametro; qualsiasi eventuale modifica di tale copia determinata dallÕesecuzione del codice del
corpo della funzione non si rifletterˆ in alcun modo sugli argomenti originariamente passati alla
funzione, come risulta chiaro dallÕesempio che segue.
Questo tipo di passaggio dei parametri consente dunque di rendere disponibile alla funzione un
valore indispensabile per le elaborazioni che avvengono al suo interno, ma le cui ÒvicissitudiniÓ
non sono di nessun interesse per il programma chiamante.
Codice
PerValore.cpp
1 #include <iostream>
2 using namespace std;
3
4 void incrementa(int num1, int num2)
5 {
6 cout<<"\n\n\t--- Ingresso nella funzione ---\n";
7 //incrementa i valori
8 ++num1;
9 ++num2;
10 cout<<"I parametri sono stati incrementati valgono "<<num1<<" "<<num2;
11 cout << "\n\t--- Uscita dalla funzione ---";
12 }
13 //INIZIO
14 int main ()
15 {
16 int numero1 = 0;
17 int numero2 = 0;
18
19 cout<<"\n\nArgomenti prima dell'invocazione = "<<numero1<<" "<<numero2;
20
21 incrementa(numero1,numero2); //invoca la funzione
22
23 cout<<"\n\nArgomenti dopo l'invocazione = "<<numero1<<" "<<numero2;
24
25 //fine programma
26 cout << "\n\nFine ";
27 system ("pause");
28 return 0;
29 }
156
Unitˆ didattica 9 - Le funzioni
Prova di esecuzione
Il passaggio dei parametri per valore, in generale, va bene per moltissime applicazioni ma, spesso, si
ha la necessitˆ di dover utilizzare le funzioni per manipolare, cio• modificare, proprio i valori dei pa-
rametri passati a esse, affinchŽ il codice chiamante possa utilizzarli per successive elaborazioni. La
tecnica utilizzata per ottenere questo scopo viene detta passaggio dei parametri per riferimento, ed •
lÕargomento del prossimo paragrafo.
157
Sezione 3 - Organizzazione degli algoritmi
LÕintestazione generale della funzione • rimasta quasi inalterata, tranne che per la modalitˆ con cui
viene passato il parametro.
Il carattere Ò&Ó che precede il parametro indica che ogni eventuale modifica effet-
tuata su quel parametro si riflette sullÕargomento dellÕinvocazione.
Nel passaggio per riferimento la funzione riceve, infatti, lÕindirizzo della locazione di memoria che
contiene il valore da elaborare, sicchŽ ogni ÒvicissitudineÓ del parametro provoca la modifica del va-
lore della variabile originariamente passata alla funzione.
LÕinvocazione della funzione non subisce alcun cambiamento, come si pu˜ vedere nella sintassi che
segue.
Codice
PerRiferimento.cpp
1 #include <iostream>
2 using namespace std;
3
4 void incrementa(int &num1, int &num2)
5 {
6 cout<<"\n\n\t--- Ingresso nella funzione ---\n";
7 //incrementa i valori
8 ++num1;
9 ++num2;
10 cout<<"I parametri sono stati incrementati valgono "<<num1<<" "<<num2;
11 cout << "\n\t--- Uscita dalla funzione ---";
12 }
13 //INIZIO
14 int main ()
15 {
16 int numero1 = 0;
17 int numero2 = 0;
18
19 cout<<"\n\nArgomenti prima dell'invocazione = "<<numero1<<" "<<numero2;
20
21 incrementa(numero1,numero2); //invoca la funzione
22
23 cout<<"\n\nArgomenti dopo l'invocazione = "<<numero1<<" "<<numero2;
24
25 //fine programma
26 cout << "\n\nFine ";
27 system ("pause");
28 return 0;
29 }
158
Unitˆ didattica 9 - Le funzioni
Prova di esecuzione
Codice
Scambia.cpp
1 #include <iostream>
2 using namespace std;
3
4 //variabili globali
5 int num1=0;
6 int num2=0;
7
8 void leggiNum()
9 {
10 cout << "\nInserire il primo valore ";
11 cin >> num1;
12
13 cout << "\nInserire il secondo valore ";
14 cin >> num2;
15 }
16
17 void scambia(int &n1, int &n2)
18 {
159
Sezione 3 - Organizzazione degli algoritmi
19 int temp;
20 //effettua lo scambio
21 temp=num1;
22 num1=num2;
23 num2=temp;
24 }
25
26 //INIZIO
27 int main ()
28 {
29 leggiNum(); //acquisisci i valori
30 cout << "\n\nArgomenti prima dell'invocazione = " << num1 << " " << num2;
31 scambia(num1, num2); //invoca la funzione per lo scambio
32 cout << "\nArgomenti dopo l'invocazione = " << num1 << " " << num2;
33
34 //fine programma
35 cout << "\n\nFine ";
36 system ("pause");
37 return 0;
38 }
Prova di esecuzione
160
Unitˆ didattica 9 - Le funzioni
9.9 Ricorsione
In C++, cos“ come in diversi altri linguaggi, una funzione pu˜ richiamare se stessa.
Quando, all'interno del corpo di una funzione, un'istruzione richiama la funzione stes-
sa, tale funzione si dice ricorsiva.
La ricorsione • un processo che definisce qualcosa in termini di se stesso e in alcuni casi viene chia-
mata definizione circolare. In matematica • facile incontrare formule ricorsive.
Prendiamo come esempo il giˆ noto fattoriale di un numero intero: il fattoriale di un numero n • il
prodotto di tutti i numeri interi da 1 a n. Il fattoriale di 3 • l × 2 × 3, ovvero 6. Il fattoriale di n si in-
dica con n!.
1 se n = 0 o n = 1
fattoriale(n) = n! =
n × fattoriale(n Ð 1) per ogni altro valore di n
Osservando la formula si vede che la condizione di arresto della ricorsione • n = 1 e che, nella parte
a destra del segno di uguale, ÒricorreÓ o ÒricompareÓ lÕinvocazione della funzione che deve essere
calcolata.
Codice
Fattoriale.cpp
1 #include "iostream"
2 using namespace std;
3
4 int fatt_iteraz(int n)
5 {
6 //variabile locale e inizializzazione
7 int fatt = 1;
8 //esegui il ciclo for
9 for (int i=1; i<=n; i++)
10 {
11 fatt = fatt * i;
12 }
13 return fatt;
14 }
15
16 int fatt_ricors (int n)
17 {
18 //variabile locale
161
Sezione 3 - Organizzazione degli algoritmi
19 int fatt;
20 if (n == 1)
21 return 1;
22 //la funzione richiama se stessa
23 fatt = n*fatt_ricors(n-1);
24 return fatt;
25 }
26
27 //INIZIO
28 int main ()
29 {
30 int num;
31 //chiedi e leggi il numero su cui calcolare il fattoriale
32 cout << "\nInserisci un numero intero (Max 10) = ";
33 cin >> num;
34
35 //scrivi il risultato della funzione con iterazione
36 cout << "\n\nFattoriale per iterazione = " << fatt_iteraz (num);
37
38 //scrivi il risultato della funzione con ricorsione
39 cout << "\nFattoriale per ricorsione = " << fatt_ricors (num);
40
41 //fine programma
42 cout << "\n\nFine ";
43 system ("pause");
44 return 0;
45 }
Prova di esecuzione
162
Unitˆ didattica 9 - Le funzioni
1. Quando si scrivono funzioni ricorsive, da qualche parte si deve prevedere unÕistruzione condizionale
(per esempio un if) che faccia in modo che la funzione termini senza eseguire alcuna ricorsione. Se
si omette tale istruzione condizionale, una volta entrati nel corpo della funzione non sarˆ pi• possibi-
le uscirne. Durante lo sviluppo del programma si consiglia di utilizzare abbondantemente le istruzio-
ni cout, in modo da sapere sempre cosa sta avvenendo e annullare lÕesecuzione nel momento in cui
si rileva un errore.
2. Spesso le routine ricorsive non riducono in modo significativo le dimensioni del codice nŽ migliorano
lÕutilizzo della memoria, rispetto alla versione iterativa. Inoltre, le versioni ricorsive della maggior par-
te delle routine vengono eseguite in modo leggermente pi• lento rispetto alle loro equivalenti iterati-
ve, a causa del sovraccarico dovuto alla continua istanziazione delle funzioni.
3. Il vantaggio principale delle funzioni ricorsive consiste nella possibilitˆ di creare versioni pi• chiare e
semplici degli algoritmi. Per esempio, lÕalgoritmo QuickSort • piuttosto difficile da implementare in
modo iterativo; inoltre, alcuni problemi, specialmente quelli di intelligenza artificiale, sono pi• adatti
a soluzioni ricorsive; infine, spesso lÕesecuzione ricorsiva risulta essere pi• lineare dellÕanalogo ite-
rativo.
...........................................................................................................................................
163
Sezione 3 - Organizzazione degli algoritmi
Fanno parte di questa libreria tutte le funzioni trigonometriche e le funzioni di matematica analitica,
che sono così immediatamente disponibili al programmatore.
FUNZIONE DESCRIZIONE
double sqrt(double x) EFx Radice quadrata
double pow(double x,double y) y Elevamento a potenza
Se x > 0, y può essere un valore qualsiasi
Se x è 0, y deve essere > 0
Se x < 0, y deve essere un intero
double sin(double x) senx Seno di x (x in radianti)
double cos(double x) cosx Coseno di x (x in radianti)
double tan(double x) tanx Tangente di x (x in radianti)
double asin(double x) sen-1 x Arco seno di x (x∈[-1,1])
double acos (double x) cos-1 x Arco coseno di x (x∈[-1,1])
double atan(double x) tan-1 x Arco tangente di x
double exp(double x) ex Esponenziale di x
double log(double x) log(x) Logaritmo naturale di x (x > 0)
double log10(double x) log10(x) Logaritmo decimaledi x (x>0)
double ceil(double x) Intero più piccolo > x
double floor(double x) Intero più grande < x
double fabs(double x) |x| Valore assoluto di x
164
Unità didattica 9 - Le funzioni
Esercizi
Unità didattica 9
2 Scrivere una funzione che calcoli e scriva l’area di un trapezio. All’interno della funzione sono presenti
l’acquisizione dei dati (base e altezza) e la scrittura dell’output (area).
3 Scrivere una funzione che calcoli e scriva l’area di un trapezio e che abbia come parametri le basi e l’al-
tezza.
4 Scrivere una funzione che acquisisca da tastiera i dati dell’esercizio precedente e stabilire l’esatta mo-
dalità di passaggio dei parametri.
6 Nella seguente funzione, che calcola il quadrato di un numero intero, compare un errore: individuarlo e
correggerlo.
int Quad(int numero)
{
int quad = 0;
quad=numero*numero;
return numero;
}
7 Scrivere una funzione che stabilisca l’aliquota IVA in relazione ai seguenti importi:
: importo fino a € 100.000 → IVA 20%;
: importo compreso tra € 100.000 fino a € 200.000 → IVA 18%;
: importo superiore a € 200.000 → IVA 16%.
8 Confermare la seguente affermazione: “I parametri passati per riferimento forniscono alla funzione una
copia esatta degli argomenti”.
9 Definire una funzione che, dato il prezzo di vendita di un prodotto e l’aliquota IVA, presenti il prezzo com-
prensivo di IVA.
q0 Definire una funzione che, dato il prezzo di vendita di un prodotto comprensivo di IVA, restituisca il prez-
zo al netto dell’IVA e l’ammontare dell’IVA (Aliquota Iva costante = 20%).
qa Dato in input un numero N, definire una funzione che restituisca la somma dei numeri dispari minori o
uguali a N.
qs Scrivere una funzione che abbia come parametri le coordinate di un punto sul piano cartesiano e che co-
munichi in output il valore della distanza del punto dall’origine degli assi.
165
Sezione 3 - Organizzazione degli algoritmi
Esercizi
Unità didattica 9
qd Data l’altezza in cm e il peso in kg di una persona, definire una funzione che scriva il messaggio:
“sottopeso” se altezza – 100 > peso;
“normale” se altezza – 100 = peso;
“soprappeso” se altezza – 100 < peso.
qf Scrivere una funzione che abbia come parametri le coordinate di un punto sul piano cartesiano e che re-
stituisca il valore della distanza del punto dall’origine degli assi.
qg Scrivere una funzione che, acquisito in input un numero, calcoli e scriva in output il quadrato e la radice
quadrata del numero (utilizzare le funzioni matematiche della libreria <cmath>).
qh Scrivere una funzione che, acquisito in input un numero, calcoli e restituisca al main come parametri il
quadrato, il cubo, la radice quadrata e la radice cubica del numero (utilizzare le funzioni matematiche del-
la libreria <cmath>).
qj Scrivere una funzione che abbia come parametri le coordinate cartesiane di due punti e che restituisca
la distanza dei due punti.
qk Scrivere una funzione che abbia come parametri le coordinate cartesiane di due punti e che calcoli le
coordinate del punto medio del segmento che unisce i due punti (scegliere il tipo di passaggio di para-
metri opportuno).
ql Dato un elenco di persone con l’indicazione del nome e dell’altezza, calcolare l’altezza media. Utilizzare
una funzione per l’acquisizione dei dati di input.
w0 Dato un elenco di persone con l’indicazione del nome e dell’altezza, indicare il nome del più alto e del
più basso. Utilizzare una funzione per l’acquisizione dei dati di input.
166
Sezione 4
Strutture dei dati
†
Obiettivi
generali
◊ Definire insiemi di dati numerabili
◊ Saper utilizzare schemi logici per organizzare insiemi
complessi di dati
◊ Costruire tabelle di dati omogenei
◊ Manipolare stringhe di caratteri
&
◊ Raggruppare dati di tipo diverso
semplici
reali ordinali
I tipi semplici possono essere float o double, oppure di tipo ordinale; i tipi ordinali possono es-
sere definiti dal programmatore attraverso i tipi enumerativi, oppure possono appartenere ai tipi or-
dinali predefiniti int, bool, char.
Il tipo ordinale si chiama cos“ perchŽ descrive un insieme finito e ordinato di valori,
che possono essere associati a numeri interi positivi.
I tipi ordinali predefiniti e i tipi float e double sono giˆ stati presentati e utilizzati in prece-
denza.
169
Sezione 4 - Strutture dei dati
Il nome dellÕenumerazione pu˜ essere utilizzato per dichiarare variabili di tale tipo, in maniera ana-
loga alle dichiarazioni di tipo viste in precedenza. Per esempio, potremo scrivere:
controlli check;
La variabile check pu˜ assumere uno qualsiasi dei valori della lista dei controlli definita in prece-
denza. Per esempio, si pu˜ scrivere:
check = gomme;
oppure
Si deve ricordare che a ognuno dei valori di una variabile enumerativa corrisponde il numero dÕordi-
ne che esso occupa allÕinterno della definizione dellÕenumerazione.
Esempio Enumera...............................................................................................................
Scrivere un programma che utilizza una variabile di tipo controlli.
Codice
Enumera.cpp
1 #include <iostream>
2 using namespace std;
3
4 //INIZIO
5 int main ()
6 {
7 //definisci l'enumerazione
8 enum controlli {freni, fari, gomme, olio, tergicristalli, carburante};
9
10 //definisci la variabile di tipo enumerativo
11 controlli check;
12
13 //assegna un valore alla variabile
14 check = gomme;
15
16 //esegui un confronto
17 if (check == gomme) cout << "controlla fari";
18
19 //scrivi il numero d'ordine del valore della variabile enumerativa
20 cout << "\nNumero d'ordine di gomme = " << check;
21
170
Unitˆ didattica 10 - Enumerazioni e array
22 //fine programma
23 cout << "\n\nFine ";
24 system ("pause");
25 return 0;
26 }
Prova di esecuzione
Fino a questo momento abbiamo visto come sia possibile definire e utilizzare in C++ tipi di dati che
abbiamo definito ÒsempliciÓ.
Se ci soffermiamo, per˜, a considerare qualche esempio un poÕ pi• complesso di quelli presentati fi-
no a ora ci rendiamo rapidamente conto che non • affatto raro incontrare la necessitˆ di gestire elen-
chi di dati.
Pensiamo per esempio a un elenco di invitati a una festa, alla lista degli studenti di una classe o agli
iscritti a una gara. In questi tre esempi siamo di fronte a un dato (cognome e nome) sempre dello stes-
so tipo che si ripete pi• volte.
Nei prossimi paragrafi presenteremo le strutture messe a disposizione da C++ per gestire non pi• da-
ti singoli, bens“ strutture di dati che raggruppano in unÕunica variabile dati diversi.
171
Sezione 4 - Strutture dei dati
Prendiamo, per esempio, l’elenco degli studenti di una classe, così come appare sul registro.
1 Abbiati Mario
2 Bonaldi Piera
3 Casati Luigi
4 Esposito Salvatore
.. ...
.. ...
24 Vivaldi Giuseppe
A ogni studente è associato un numero di posizione e ogni numero individua uno studente.
In questo esempio, ogni elemento del vettore è di tipo stringa (per contenere il cognome e nome del-
lo studente) e ogni elemento è individuato da un numero, detto indice. La dimensione del vettore è 24.
Oppure consideriamo un quadrilatero irregolare in cui ogni lato ha una misura diversa.
4
3
1 2
Ogni elemento dell’esempio precedente è un dato di tipo numerico (la misura del lato) e per ogni
misura è presente un indice che è il numero del lato; la dimensione è 4.
Riprendendo gli esempi del paragrafo precedente, per definire il vettore che contiene i nomi dei 24
studenti di una classe possiamo scrivere:
int dim = 24;
string Studenti [dim];
172
Unitˆ didattica 10 - Enumerazioni e array
é buona norma definire la dimensione del vettore come variabile (nel nostro caso: dim): in questo
modo eventuali variazioni della dimensione richiedono un solo intervento nel codice.
Si ricordi che lÕintervallo dei valori possibili per lÕindice di un vettore parte da 0 e arri-
va fino a dimensione Ð1.
In base a questa regola, nellÕesempio dellÕelenco degli studenti il primo studente (Abbiati Mario) •
individuato dallÕindice 0 e lÕultimo (Vivaldi Giuseppe) dallÕindice 23.
Quando si lavora con una variabile di tipo vettore, occorre sempre, allÕinterno del programma, indi-
care sia il nome della variabile sia lÕindice che individua la componente del vettore che vogliamo trat-
tare; per esempio, per assegnare al primo elemento del vettore la misura del primo lato, si scrive:
misure[0] = 15
Si noti che lÕindice va indicato racchiuso tra parentesi quadre dopo il nome della variabile.
Per assegnare a ciascun lato la misura corrispondente, si scrive:
misure[1] = 8
misure[2] = 7
misure[3] = 16
Dal punto di vista concettuale, possiamo pensare che in memoria • presente una struttura di questo
tipo:
VETTORE
misure → 15 8 7 16
indice → 0 1 2 3
Per sommare le misure del primo e del quarto lato, si utilizza la seguente istruzione:
Spesso nasce lÕesigenza di accedere a tutti gli elementi di un vettore o, come si usa dire, di ÒvisitareÓ
tutti gli elementi del vettore per poter eseguire una elaborazione su ciascuno di essi. In questo caso,
torna molto utile ricorrere alla struttura della ripetizione con contatore: attraverso di essa si utilizza
un indice che assume tutti i valori che vanno da 0 a dimensione Ð1 del vettore e che serve per riferir-
si a ciascun elemento.
173
Sezione 4 - Strutture dei dati
Pseudocodifica
//struttura dati
vettore delle misure dei lati
//input
//tutti i dati sono definiti allÕinterno del programma
//output
perimetro
//variabili di lavoro
i //indice per ciclo for
INIZIO
//inizializza il vettore delle misure
misure[0] ← 15
misure[1] ← 8
misure[2] ← 7
misure[3] ← 16
//inizializza perimetro
perimetro ← 0
//visita il vettore
PER i DA 0 A dim Ð 1
perimetro = perimetro + misure[i] //somma le misure ad 1 ad 1
FINE PER
scrivi (Perimetro)
FINE
Codice
Perimetro.cpp
1 #include <iostream>
2 using namespace std;
3
4 //INIZIO
5 int main ()
6 {
7 //struttura del vettore
8 const int dim=4;
9 int misure[dim];
10
11 int i; //contatore per ciclo for
12 int perim; //dato di output
13
14 //inizializza il vettore misure
174
Unitˆ didattica 10 - Enumerazioni e array
15 misure[0] = 15;
16 misure[1] = 8;
17 misure[2] = 7;
18 misure[3] = 16;
19
20 //inizializza perimetro
21 perim = 0;
22
23 //visita il vettore
24 for(i=0; i<dim; i++)
25 {
26 perim = perim + misure[i]; //somma le misure ad 1 ad 1
27 }
28
29 //scrivi perimetro
30 cout<<"Perimetro = "<<perim;
31
32 //fine programma
33 cout << "\n\nFine ";
34 system ("pause");
35 return 0;
36 }
Prova di esecuzione
Le istruzioni di inizializzazione usate nellÕesempio precedente possono essere sostituite da una scrittu-
ra pi• compatta. Nel codice Perimetro.cpp le righe da 15 a 18 servono per assegnare i valori iniziali
alle componenti del vettore misure. Le riportiamo di seguito.
misure[0] = 15;
misure[1] = 8;
misure[2] = 7;
misure[3] = 16;
LÕoperazione precedente pu˜ essere realizzata assegnando direttamente i valori delle componenti in fa-
se di definizione del vettore. Quindi le sette righe di codice
7 const int dim=4;
8 int misure[dim];
175
Sezione 4 - Strutture dei dati
. . .
14 //inizializza il vettore misure
15 misure[0] = 15;
15 misure[1] = 8;
16 misure[2] = 7;
17 misure[3] = 16;
18 . . .
...........................................................................................................................................
Pseudocodifica
//costanti
dimensione = 5
//struttura dati
vettore di 5 interi
//input
serie di 5 numeri
//output
vettore di 5 interi
INIZIO
PER ogni elemento del vettore
richiedi dato
carica dato
FINE PER
PER ogni elemento del vettore
scrivi (elemento)
FINE PER
FINE
176
Unitˆ didattica 10 - Enumerazioni e array
Codice
arrayCarica.cpp
1 #include <iostream>
2 using namespace std;
3
4 //INIZIO
5 int main ()
6 {
7 //definisci il vettore di 5 elementi
8 const int dim = 5;
9
10 //definisci la struttura dati
11 int vett [dim];
12
13 //ciclo for x caricare vettore
14 for (int i=0; i<dim; i++)
15 {
16 //richiedi un dato
17 cout<<"Inserisci un dato ";
18 cin>>vett[i];
19 }
20
21 cout<<"\nContenuto del vettore: ";
22
23 //visita il vettore
24 for (int i=0; i<dim; i++)
25 {
26 cout<<" "<<vett[i]; //scrivi elemento di posto i
27 }
28 cout<<"\nVettore visualizzato";
29
30 //fine programma
31 cout << "\n\nFine ";
32 system ("pause");
33 return 0;
34 }
Prova di esecuzione
177
Sezione 4 - Strutture dei dati
Alla riga 18 il dato inserito da tastiera viene assegnato allÕelemento vett con indice i, dove i, gra-
zie al ciclo for, assume, di volta in volta, i valori che vanno da 0 a 4.
Alla riga 24 la scrittura degli elementi del vettore viene realizzata con un secondo ciclo for.
Alla riga 26 ogni elemento del vettore viene scritto sulla stessa riga dei precedenti. Solo alla fine del
ciclo for, alla riga 28, si va a capo per scrivere una nuova riga di messaggio.
...........................................................................................................................................
Esempio arrayVaria............................................................................................................
Permettere lÕinserimento da tastiera dei valori di un vettore di interi. Anche la dimensione
del vettore • acquisita da tastiera e deve essere inferiore a 20.
Pseudocodifica
//costanti
dimensione massima = 20
//struttura dati
vettore di interi
//input
dimensione
serie di n interi (n = dimensione)
//output
vettore di interi
INIZIO
leggi (dimensione)
PER ogni elemento del vettore
chiedi dato
carica dato
FINE PER
PER ogni elemento del vettore
scrivi (elemento)
FINE PER
FINE
Codice
arrayVaria.cpp
1 #include <iostream>
2 using namespace std;
178
Unitˆ didattica 10 - Enumerazioni e array
3
4 //INIZIO
5 int main ()
6 {
7 const int MAX = 20; //dimensione massima per il vettore
8 int dim;
9 int i;
10
11 //definisci il vettore
12 int vett[MAX];
13
14 //acquisisci e controlla la dimensione
15 do
16 {
17 cout<<"\nInserisci la dimensione (max = 20) "
18 cin>>dim;
19 }
20 //controlla il rispetto dei limiti
21 while (dim < 1 | dim > MAX);
22
23 //salta una riga
24 cout<<"\n";
25
26 for (i=0; i<dim; i++)
27 {
28 //richiedi un dato e caricalo nel vettore
29 cout<<"Inserisci un dato ";
30 cin>>vett[i];
31 }
32
33 cout<<"\nContenuto del vettore: ";
34 //visita il vettore
35 for (i=0; i<dim; i++)
36 {
37 cout<<" "<<vett[i];
38 }
39 cout<<"\nVettore visualizzato";
40
41 //fine programma
42 cout << "\n\nFine ";
43 system ("pause");
44 return 0;
45 }
Prova di esecuzione
179
Sezione 4 - Strutture dei dati
10.7 Matrici
Nei paragrafi precedenti abbiamo visto come lavorare con array a una dimensione, vale a dire con ar-
ray che hanno un unico indice di individuazione degli elementi che li compongono.
é possibile, tuttavia, definire anche array a due o pi• dimensioni.
Solitamente, gli array a una dimensione prendono il nome di vettori, mentre gli array
a due dimensioni sono detti matrici.
Come giˆ visto, un array a una dimensione • definito da un nome e da una dimensione, con una sin-
tassi del tutto simile a quella riportata di seguito.
VETTORE
Dati → 37 53 11 28
Indice → 0 1 2 3
Per definire una matrice, invece, occorre specificare due o pi• dimensioni.
Una matrice • un insieme di dati dello stesso tipo organizzati in una griglia: ogni ele-
mento che compone la matrice • individuato dallÕindice di riga e dallÕindice di co-
lonna in cui lÕelemento • posizionato.
In C++ la definizione della struttura di una matrice • analoga alla definizione di un array a una di-
mensione. Per esempio, se si scrive:
int matrice[3][4]
180
Unitˆ didattica 10 - Enumerazioni e array
sÕintende che la struttura di nome matrice • composta da 3 righe e 4 colonne, come mostrato nel-
lÕesempio che segue.
MATRICE
0 7 37 24 3
Righe 1 45 12 18 81
2 11 53 37 28
0 1 2 3
Colonne
Per elaborare un singolo elemento della matrice occorre specificare il numero di riga e il numero di
colonna. Con riferimento alla matrice precedente, si pu˜ dire che 18 • contenuto nella cella indivi-
duata da matrice[1][2].
Pseudocodifica
Costante MAX = 20 //limite massimo per le dimensioni della matrice
//struttura dati
Matrice di interi
//input
Righe
Colonne
181
Sezione 4 - Strutture dei dati
INIZIO
//acquisisci numero delle righe
RIPETI
leggi (righe)
//controlla rispetto dei limiti
MENTRE righe < 1 OR righe > 20
//visita la matrice
PER i DA 0 A righe Ð 1
PER j DA 0 A colonne Ð 1
scrivi (matrice [i, j])
FINE PER
vai a capo
FINE PER
FINE
Codice
MatriceCarica.cpp
1 #include <iostream>
2 #include <iomanip>
3 using namespace std;
4
5 //INIZIO
6 int main ()
7 {
8 const int MAX = 20; //dimensione massima per la matrice
9
10 //definisci la matrice
11 int mat[MAX][MAX];
12 int righe;
13 int colonne;
182
Unitˆ didattica 10 - Enumerazioni e array
14 int i;
15 int j;
16
17 //acquisisci numero delle righe
18 do
19 {
20 cout<<"\nInserisci il numero righe (max="<<MAX<<") ";
21 cin>>righe;
22 }
23 //controlla il rispetto dei limiti
24 while (righe < 1 | righe > MAX);
25
26 //acquisisci numero delle colonne
27 do
28 {
29 cout<<"\nInserisci il numero colonne (max="<<MAX<<") ";
30 cin>>colonne;
31 }
32 //controlla il rispetto dei limiti
33 while (colonne < 1 | colonne > 20);
34
35 cout<<"\n";
36
37 //carica dati nella matrice
38 for (i=0; i<righe; i++) //per ogni riga della matrice
39 {
40 for (j=0; j<colonne; j++) //per ogni elemento della riga
41 {
42 //richiedi un dato e caricalo nella matrice
43 cout<<"Inserisci un dato ";
44 cin>>mat [i][j];
45 }
46 }
47
48 cout<<"\nContenuto della matrice: \n";
49 //visita la matrice
50 for (i=0; i<righe; i++) //per ogni riga della matrice
51 {
52 for (j=0; j<colonne; j++) //per ogni elemento della riga
53 {
54 //scrivi gli elementi di una riga
55 //uno di seguito all'altro, con ingombro pari a 6 caratteri
56 cout<<setw(6)<<mat[i][j];
57 }
58 //alla fine della riga va a capo
59 cout<<"\n";
60 }
61
62 cout<<"\nmatrice visualizzata";
63
64 //fine programma
65 cout << "\n\nFine ";
66 system ("pause");
67 return 0;
68 }
183
Sezione 4 - Strutture dei dati
Prova di esecuzione
Allo scopo di acquisire dimestichezza con i problemi relativi al trattamento delle matrici, viene pre-
sentato un secondo esempio.
Esempio TotaleRighe..........................................................................................................
Caricare i dati di una matrice di interi con numero di righe e numero di colonne non supe-
riore a 20. Scrivere il contenuto della matrice e indicare per ogni riga il totale dei valori in
essa contenuti.
184
Unitˆ didattica 10 - Enumerazioni e array
Si definisca una variabile totRiga in cui accumulare i valori di ciascuna riga e, con riferimento al
segmento di codice relativo alla scrittura della matrice, si inseriscano le istruzioni che servono per:
1. inizializzare totRiga;
2. sommare in totRiga i valori di una riga della matrice;
3. scrivere totRiga.
Pseudocodifica
Costante Max = 20 //limite massimo per le dimensioni della matrice
//struttura dati
matrice di interi
//input
Righe
Colonne
Serie di numeri per caricare la matrice
//output
matrice
totaleRiga
//variabili di lavoro
i indice di riga
j indice di colonna
INIZIO
....
//parte uguale allÕesempio precedente
....
//visita la matrice
PER i DA 0 A righe Ð 1
totRiga ← 0
PER j DA 0 A colonne Ð 1
scrivi (matrice [i, j])
totRiga ← totRiga + mat [i, j]
FINE PER
scrivi (totRiga) e vai a capo
FINE PER
FINE
Codice
TotaleRighe.cpp
1 #include <iostream>
2 #include <iomanip>
3 using namespace std;
4
5 //INIZIO
6 int main ()
7 {
8 const int MAX = 20; //dimensione massima per la matrice
9
10 //definisci la matrice
11 int mat[MAX][MAX];
12 int righe;
185
Sezione 4 - Strutture dei dati
13 int colonne;
14 int i;
15 int j;
16 int totRiga;
17
18 //acquisisci numero delle righe
19 do
20 {
21 cout<<"\nInserisci il numero righe (max="<<MAX<<") ";
22 cin>>righe;
23 }
24 //controlla il rispetto dei limiti
25 while (righe < 1 | righe > MAX);
26
27 //acquisisci il numero delle colonne
28 do
29 {
30 cout<<"\nInserisci il numero colonne (max="<<MAX<<") ";
31 cin>>colonne;
32 }
33 //controlla il rispetto dei limiti
34 while (colonne < 1 | colonne > MAX);
35
36 //salta una riga
37 cout<<"\n";
38
39 //carica la matrice
40 for (i=0; i<righe; i++) //per ogni riga della matrice
41 {
42 for (j=0; j<colonne; j++) //per ogni elemento della riga
43 {
44 //richiedi un dato e caricalo nella matrice
45 cout<<"Inserisci un dato ";
46 cin>>mat [i][j];
47 }
48 }
49
50 cout<<"\nContenuto della matrice: \n";
51 cout<<setw(14)<<"Totale"<<endl;
52
53 //visita la matrice
54 for (i=0; i<righe; i++) //per ogni riga della matrice
55 {
56 totRiga = 0;
57 for (j=0; j<colonne; j++) //per ogni elemento della riga
58 {
59 //scrivi gli elementi di una riga
60 //uno di seguito all'altro, con ingombro pari a 4 caratteri
61 cout<<setw(4)<<mat[i][j];
62
63 //accumula i valori in totRiga
64 totRiga = totRiga + mat[i][j];
65 }
66 //alla fine della riga scrivi il totale
67 //e vai a capo
186
Unitˆ didattica 10 - Enumerazioni e array
68 cout<<setw(6)<<totRiga<<endl;
69 }
70
71 cout<<"\nMatrice visualizzata";
72
73 //fine programma
74 cout << "\n\nFine ";
75 system ("pause");
76 return 0;
77 }
Prova di esecuzione
Esempio funzioniArray........................................................................................................
Acquisire da tastiera un vettore di dimensione non superiore a 20 righe e stamparlo. Per la
realizzazione del programma utilizzare tre funzioni, i cui nomi sono chiediDim, leggi-
Vettore, scriviVettore.
187
Sezione 4 - Strutture dei dati
Codice
funzioniArray.cpp
1 #include <iostream>
2 using namespace std;
3
4 const int MAX = 20; //dimensione massima per il vettore
5 int dim; //dimensione acquisita da tastiera
6 int i;
7
8 //funzione per leggere la dimensione
9 int chiediDim (int &d)
10 {
11 //acquisisci e controlla la dimensione
12 do
13 {
14 //chiedi e leggi la dimensione
15 cout<<"\nInserisci la dimensione ";
16 cin>>d;
17 }
18 //controlla il rispetto dei limiti
19 while (d < 1 | d > MAX);
20 return d;
21 }
22
23 //funzione per leggere il vettore
24 void leggiVettore (int v[], int d)
25 {
26 for (i=0; i<d; i++)
27 {
28 //richiedi un dato e caricalo nel vettore
29 cout<<"Inserisci un dato ";
30 cin>>v[i];
31 }
32 }
33
34 //funzione per scrivere il vettore
35 void scriviVettore (int v[], int d)
36 {
37 cout<<"\nContenuto del vettore: ";
38 //visita il vettore
39 for (i=0; i<d; i++)
40 {
41 cout<<" "<<v[i];
42 }
43 cout<<"\nVettore visualizzato";
44 }
45
188
Unitˆ didattica 10 - Enumerazioni e array
46 //INIZIO
47 int main ()
48 {
49 //definisci il vettore con dimensione MAX
50 int vett[MAX];
51
52 //richiama la funzione per acquisire la dimensione del vettore
53 dim = chiediDim (dim);
54
55 //salta una riga
56 cout<<"\n";
57
58 //richiama la funzione leggiVettore
59 leggiVettore(vett,dim);
60
61 //richiama la funzione scriviVettore
62 scriviVettore(vett,dim);
63
64 //fine programma
65 cout << "\n\nFine ";
66 system ("pause");
67 return 0;
68 }
Prova di esecuzione
Risulta del tutto uguale a quella dellÕesempio ArrayVaria.
Nel caso in cui si voglia lavorare con array a pi• di una dimensione i richiami alla funzione leggiVetto-
re (che in questo caso si pu˜ chiamare leggiMatrice) mantengono la stessa sintassi indicata nellÕe-
sempio, con lÕargomento privo di dimensioni, mentre nellÕintestazione della funzione il parametro che si ri-
ferisce alla struttura multidimensionale DEVE contenere la specifica di tutte le dimensioni tranne la prima.
Per esempio, se si vuole lavorare sulla matrice quadrata matr di ordine MAX, per richiamare la funzione
leggiMatrice si deve scrivere lÕistruzione che segue.
leggiMatrice(matr, . . . );
mentre lÕintestazione della funzione assume la forma indicata di seguito.
void scriviMatrice (int m[][MAX], . . . )
...........................................................................................................................................
189
Sezione 4 - Strutture dei dati
Esercizi
Unità didattica 10
1 Dopo aver caricato in memoria un vettore di interi con dimensione d (con d inserito da tastiera), calco-
lare la somma dei valori contenuti nel vettore.
2 Dopo aver caricato in memoria un vettore di interi con dimensione d (con d inserito da tastiera), azzera-
re il primo elemento del vettore.
3 Dopo aver caricato in memoria un vettore di interi con dimensione d (con d inserito da tastiera), azzera-
re l’ultimo elemento del vettore.
4 Dopo aver caricato in memoria un vettore di interi con dimensione d (con d inserito da tastiera), azzera-
re l’elemento di posto n, con n dato in input.
5 Dopo aver caricato in memoria un vettore di interi con dimensione d (con d inserito da tastiera), calco-
lare la media dei valori contenuti nel vettore.
6 Dopo aver caricato in memoria un vettore di interi con dimensione d (con d inserito da tastiera), calco-
lare la media dei valori contenuti nel vettore. Successivamente scrivere gli elementi del vettore che han-
no valore superiore alla media.
7 Dopo aver caricato in memoria un vettore di interi con dimensione d (con d inserito da tastiera), calco-
lare la media dei valori contenuti nel vettore. Successivamente contare gli elementi del vettore che han-
no valore superiore alla media.
8 Dopo aver caricato in memoria un vettore di interi con dimensione d (con d inserito da tastiera), calco-
lare la media dei valori contenuti nel vettore. Successivamente creare un nuovo vettore che contenga gli
elementi del vettore iniziale che hanno valore superiore alla media.
9 Dopo aver caricato in memoria un vettore di interi con dimensione d (con d inserito da tastiera), scrive-
re gli elementi pari contenuti nel vettore.
q0 Dopo aver caricato in memoria un vettore di interi con dimensione d (con d inserito da tastiera), scrive-
re gli elementi di posto pari contenuti nel vettore.
qa Dopo aver caricato in memoria un vettore di interi con dimensione d (con d inserito da tastiera), creare
un nuovo vettore che contenga gli elementi pari del vettore iniziale.
qs Dopo aver caricato in memoria un vettore di interi con dimensione d (con d inserito da tastiera), inserire
“in testa” al vettore un nuovo elemento. Scrivere il vettore iniziale e il vettore modificato. (Suggerimen-
ti: poiché deve essere inserito un nuovo elemento il vettore deve essere definito con dimensione pari a
d + 1; “in testa” al vettore vuol dire al posto 0 del vettore; per fare spazio al nuovo elemento, i dati pree-
sistenti devono essere spostati di un posto a destra).
qd Dopo aver caricato in memoria un vettore di interi con dimensione d (con d inserito da tastiera), inserire
“in coda” al vettore un nuovo elemento. Scrivere il vettore iniziale e il vettore modificato. (Suggerimenti:
poiché deve essere inserito un nuovo elemento il vettore deve essere definito con dimensione pari a d
+ 1; “In coda” al vettore vuol dire dopo l’ultimo elemento del vettore; per fare spazio al nuovo elemen-
to, basta aumentare la dimensione iniziale).
190
Unità didattica 10 - Enumerazioni e array
Esercizi
Unità didattica 10
qf Dopo aver caricato in memoria un vettore di interi con dimensione d (con d inserito da tastiera), elimi-
nare l’ultimo elemento del vettore. Scrivere il vettore iniziale e il vettore modificato. (Suggerimenti: ba-
sta decrementare la dimensione del vettore).
qg Dopo aver caricato in memoria un vettore di interi con dimensione d (con d inserito da tastiera), elimi-
nare il primo elemento del vettore. Scrivere il vettore iniziale e il vettore modificato. (Suggerimenti: si trat-
ta di spostare tutti gli elementi del vettore di un posto a sinistra e, successivamente, diminuire la di-
mensione del vettore).
qh Dopo aver caricato in memoria una matrice con dimensioni date in input non superiori a 20, scrivere l’e-
lemento di posto [r,c] (con r e c inserite da tastiera).
qj Dopo aver caricato in memoria una matrice con dimensioni date in input non superiori a 20, scrivere gli
elementi della riga k (con k dato in input).
qk Dopo aver caricato in memoria una matrice con dimensioni date in input non superiori a 20, scrivere gli
elementi della colonna k (con k dato in input).
ql Dopo aver caricato in memoria una matrice con dimensioni date in input non superiori a 10, calcolare i
totali di colonna.
w0 Dopo aver caricato in memoria una matrice quadrata di ordine n (con n dato in input non superiore a 10),
scrivere gli elementi che stanno sulla diagonale principale (per matrice quadrata di ordine n si intende
una matrice con numero-righe = numero-colonne = n; gli elementi che stanno sulla diagonale principale
hanno indice di riga e indice di colonna uguali).
191
Unitˆ didattica
11
Stringhe e strutture
1 1.1 D e fi n i z i o n e d i s t r i n g a
Insieme ai dati di tipo numerico, le stringhe rappresentano il tipo di dati pi• utilizzato nei programmi.
Una stringa • una sequenza di caratteri, come ÒHelloÓ. In C++ le stringhe sono rac-
chiuse tra virgolette doppie, che non sono considerate parte della stringa.
é possibile dichiarare variabili destinate a contenere stringhe, utilizzando istruzioni come quella che
segue.
Il tipo string • un tipo standard di C++; per utilizzarlo • sufficiente includere il riferimento al-
lÕheader file <string>, utilizzando la direttiva riportata di seguito.
#include <string>
LÕoperazione di assegnazione permette di definire la stringa che deve essere contenuta da una varia-
bile di tipo string, come illustra lÕistruzione di esempio che segue.
nome = ÒCarloÓ ;
é anche possibile leggere una stringa da tastiera: il frammento di codice seguente mostra come.
In questo caso nella variabile di tipo stringa viene memorizzata una sola parola: infatti, tenendo pre-
sente che le parole sono considerate dal C++ divise dai separatori (caratteri di spaziatura, di tabula-
zione, di a capo...), se lÕutente digita ÒMario BianchiÓ in risposta al prompt, nella variabile nome vie-
ne memorizzata la sola parola ÒMarioÓ. Per leggere la stringa successiva si deve utilizzare una se-
conda istruzione di input.
Questo vincolo complica la scrittura di unÕistruzione di input che possa trattare correttamente le ri-
sposte di tipo stringa fornite dallÕutente in risposta a una domanda: in alcuni casi • possibile che lÕu-
tente digiti solo il nome, in altri il nome seguito dal cognome, in altri ancora possono essere presen-
ti le iniziali di un secondo nome.
Questa situazione pu˜ essere risolta mediante il comando getline.
LÕistruzione
getline(cin, nome);
legge tutti i caratteri digitati fino a che si preme INVIO e genera una stringa che con-
tiene tutti i caratteri e che viene memorizzata nella variabile nome.
193
Sezione 4 - Strutture dei dati
Codice
LeggiNome.cpp
1 #include <iostream>
2 #include <string>
3 using namespace std;
4
5 //INIZIO
6 int main ()
7 {
8 //definisci una variabile stringa
9 string nome;
10
11 //chiedi e leggi il nome
12 cout << Ò\nInserisci il tuo nome => Ò;
13 cin >> nome;
14
15 //scrivi il contenuto della variabile nome
16 cout << Ò\nla variabile nome contiene Ò << nome;
17
18 //fine programma
19 cout << Ò\n\nFine Ò;
20 system (ÒpauseÓ);
21 return 0;
22 }
Prova di esecuzione
194
Unitˆ didattica 11 - Stringhe e strutture
Codice
NomeCompleto.cpp
1 #include <iostream>
2 #include <string>
3 using namespace std;
4
5 //INIZIO
6 int main ()
7 {
8 //definisci una variabile stringa
9 string nome;
10
11 //chiedi e leggi tutta la risposta dellÕutente
12 cout << Ò\nInserisci il tuo nome => Ò;
13 getline(cin,nome);
14
15 //scrivi il contenuto della variabile nome
16 cout << Ò\nla variabile nome contiene Ò << nome;
17
18 //fine programma
19 cout << Ò\n\nFine Ò;
20 system (ÒpauseÓ);
21 return 0;
22 }
Prova di esecuzione
1 1.2 L u n g h e z z a d i u n a s t ri n g a
NellÕesempio appena visto la variabile nome contiene la stringa ÒMario BianchiÓ, che • composta da
12 caratteri compreso lo spazio.
Per esempio, la lunghezza della stringa ÒMario BianchiÓ • 13, mentre la lunghezza di ÒHello,
World!\nÓ • 14, dato che il carattere di escape Ò\nÓ • considerato unico.
195
Sezione 4 - Strutture dei dati
A differenza della funzione getline, la funzione length • richiamata con la cosiddetta Ònotazio-
ne puntoÓ: si deve per prima cosa scrivere il nome della stringa di cui si vuole calcolare la lunghezza,
poi si inserisce un punto e infine il nome della funzione, seguiti da una coppia di parentesi. Un esem-
pio • riportato di seguito.
int n = nome.length();
Anticipando la terminologia che sarˆ trattata pi• avanti, nellÕUnitˆ didattica 12, relativa alla pro-
grammazione a oggetti, si precisa che length() • un metodo della classe string che si applica a
un oggetto come nome.
Codice
Lunghezza.cpp
1 #include <iostream>
2 #include <string>
3 using namespace std;
4
5 //INIZIO
6 int main ()
7 {
8 //definisci una variabile stringa
9 string nome;
10
11 //chiedi e leggi tutta la risposta dellÕutente
12 cout << Ò\nInserisci il tuo nome => Ò;
13 getline(cin,nome);
14
15 //scrivi il contenuto della variabile nome
16 cout << Ò\nLunghezza della risposta = Ò << nome.length();
17
18 //fine programma
19 cout << Ò\n\nFine Ò;
20 system (ÒpauseÓ);
21 return 0;
22 }
Prova di esecuzione
196
Unitˆ didattica 11 - Stringhe e strutture
1 1.3 C o n c a t e n a zi o n e e d e s t r a zi o n e
é possibile unire due stringhe utilizzando lÕoperatore Ò+Ó che, applicato alle stringhe, si limita ad ac-
codare una stringa a unÕaltra stringa.
LÕoperazione che permette di unire pi• stringhe per formarne una unica prende il no-
me di concatenazione.
Codice
Concatena.cpp
1 #include <iostream>
2 #include <string>
3 using namespace std;
4
5 //INIZIO
6 int main ()
7 {
8 //dichiara due stringhe
9 string stringa1 = ÒBenvenutiÓ;
10 string stringa2 = Òin laboratorioÓ;
11
12 //concatena le stringhe
13 stringa1 = stringa1 + stringa2;
14
15 //scrivi il risultato
16 cout << Ò\nStringa risultante = Ò << stringa1;
17
18 //fine programma
19 cout << Ò\n\nFine Ò;
20 system (ÒpauseÓ);
21 return 0;
22 }
Prova di esecuzione
197
Sezione 4 - Strutture dei dati
...........................................................................................................................................
Così come è possibile unire stringhe corte per formarne una lunga, si possono estrarre stringhe se-
condarie da una stringa iniziale più lunga.
Per estrarre una stringa secondaria (detta anche sottostringa) da una stringa princi-
pale si utilizza la funzione substr.
Codice
Estrai.cpp
1 #include <iostream>
2 #include <string>
3 using namespace std;
4
5 //INIZIO
6 int main ()
7 {
8 //stringa di saluto
9 string stringa = “Hello, world!”;
10
11 //estrai la prima parola
12 string sottoStringa = stringa.substr(0,5);
13
14 //scrivi il risultato dell’estrazione
15 cout << “\nStringa estratta = “ << sottoStringa;
16
17 //fine programma
18 cout << “\n\nFine “;
198
Unitˆ didattica 11 - Stringhe e strutture
19 system (ÒpauseÓ);
20 return 0;
21 }
Prova di esecuzione
Codice
Iniziali.cpp
1 #include <iostream>
2 #include <string>
3 using namespace std;
4
5 //INIZIO
6 int main ()
7 {
8 //dichiara le due stringhe nome e cognome
9 string nome;
10 string cognome;
11
12 //chiedi e leggi il nome e il cognome
13 cout << ÒInserisci nome e cognome => Ò;
14 cin >> nome >> cognome;
15
16 //estrai le lettere iniziali e concatenale
17 string iniziali = nome.substr(0, 1) + Ò.Ó + cognome.substr(0, 1) + Ò.Ó;
18
19 //scrivi il risultato
20 cout << Ò\nLe iniziali sono Ò << iniziali;
21
22 //fine programma
23 cout << Ò\n\nFine Ò;
24 system (ÒpauseÓ);
25 return 0;
26 }
199
Sezione 4 - Strutture dei dati
Prova di esecuzione
1 1.4 C o n f r o n t i t r a s t ri n g h e
Ogni istruzione if verifica una condizione, che in molti casi consiste nel confronto tra due valo-
ri: per esempio, si può verificare la condizione che “area < 10 e area >= 0”. I simboli “<” e “>=”
sono definiti operatori relazionali. Il C++ dispone di sei operatori relazionali, riepilogati nella ta-
bella che segue.
O P E R A T O R E D E S C RIZIO N E
== Uguale a
< Minore di
> Maggiore di
<= Minore o uguale a
>= Maggiore o uguale a
!= Diverso
Come si può osservare, solo due degli operatori relazionali (> e <) sono simili alle notazioni
matematiche.
L’operatore == può inizialmente confondere la maggior parte dei neofiti di C++, tuttavia in C++
l’operatore = ha già un significato specifico, dato che è l’operatore di assegnazione. L’operatore
== indica invece la relazione di uguaglianza, come risulta evidente nelle due righe di codice che
seguono.
a = 5; //operazione di assegnazione
if (a== 5) //verifica se a è uguale a 5
if (name == “Harry”)
200
Unitˆ didattica 11 - Stringhe e strutture
In C++ la differenza tra lettere maiuscole e minuscole • importante: per esempio, ÒHarryÓ e
ÒHARRYÓ non sono la stessa stringa.
Nel confronto tra stringhe, gli operatori <, <=, > e >= seguono le regole dellÕordinamento alfabeti-
co. Se si scrive
la condizione indicata dopo if risulta falsa perchŽ, nellÕordinamento alfabetico, ÒDickÓ viene prima
(cio• • ÒminoreÓ) di Tom. In realtˆ, lÕordine alfabetico utilizzato dal C++ • diverso da quello di un
normale dizionario: il C++, infatti, • sensibile alle maiuscole e ordina i caratteri iniziando dai nume-
ri, seguiti dai caratteri maiuscoli e da quelli minuscoli: per esempio, la cifra Ò1Ó viene prima del ca-
rattere ÒBÓ, che viene prima di ÒaÓ. Il carattere spazio viene prima di tutti gli altri caratteri.
Volendo essere rigorosi si dovrebbe precisare che lÕordinamento dei caratteri dipende dal sistema
operativo utilizzato: la maggioranza dei sistemi operativi per PC utilizza il cosiddetto codice ASCII
(American Standard Code for Information Interchange) oppure una delle sue estensioni, i cui carat-
teri sono ordinati come • stato descritto.
Quando si confrontano due stringhe, le lettere corrispondenti vengono confrontate finchŽ termina
una delle stringhe o si riscontra la prima differenza: se termina una delle stringhe, quella pi• lunga
viene disposta dopo lÕaltra (cio•, risulta essere maggiore dellÕaltra), mentre se viene rilevata unÕerra-
ta corrispondenza tra i caratteri si rende necessario confrontarli per determinare quale stringa • la
successiva nella sequenza lessicale. QuestÕultimo processo viene definito confronto lessicografico.
Se, per esempio, si confrontano le stringhe ÒautoÓ e ÒautomaÓ, le prime quattro lettere corrispon-
dono e si raggiunge il termine della prima stringa. Pertanto la stringa ÒautoÓ precede la stringa Òau-
tomaÓ, secondo la convenzione di ordinamento spiegata sopra.
Confrontiamo ora le stringhe Ò autoritˆÓ e ÒautomaÓ. Anche in questo caso le prime quattro lettere
corrispondono ma, dal momento che la ÒrÓ viene dopo la ÒmÓ, la stringa ÒautoritˆÓ segue la parola
ÒautomaÓ.
é possibile confrontare solo i numeri con i numeri e le stringhe con le stringhe. Il test indicato di se-
guito, per esempio,
non • valido.
1 1.5 C a r a t t e ri e s t ri n g h e C
Il C++ prevede che i singoli caratteri siano di tipo char. Nel linguaggio C, il precursore di C++, lÕu-
nico modo per implementare le stringhe consiste nel definire array di caratteri.
Nel codice C++ • possibile distinguere le stringhe di tipo C, poichŽ sono definite come di tipo char*
oppure come array (char[]).
Si ricordi, inoltre, che in C i singoli caratteri sono racchiusi tra virgolette semplici: per esempio, la strin-
gaÔaÕ identifica il carattere a mentre ÒaÓ corrisponde a una stringa che contiene il singolo carattere ÔaÕ.
LÕutilizzo di array di caratteri per definire le stringhe implica una complicazione significativa per i
programmatori, che devono implementare manualmente lo spazio di memoria da riservare per
queste sequenze: in C • comune lÕerrore di memorizzare una stringa in una variabile troppo pic-
cola per contenerne tutti i caratteri. Non essendo possibile verificare questa eventualitˆ, • preve-
dibile che il programmatore poco esperto possa sovrascrivere le aree di memoria destinate ad altre
variabili.
201
Sezione 4 - Strutture dei dati
Le stringhe C++ standard gestiscono invece questa complicazione in modo completamente automa-
tico. Nella maggior parte delle attivitˆ di programmazione, infatti, non • necessario ricorrere al tipo
di dati char e si possono utilizzare stringhe di lunghezza unitaria per definire singoli caratteri.
1 1.6 D i c h i a r a zi o n e d i u n a s t r u t t u r a
Le strutture sono particolari tipi di dati che vengono definiti aggregati, cio• capaci di contenere tipi
di dati diversi.
Dichiarare un oggetto di tipo struttura significa avere a che fare con un nuovo tipo di dato.
I membri della struttura altro non sono che variabili. Di solito le strutture vengono utilizzate per ge-
stire quantitˆ di dati non molto grandi e che occupano poca memoria.
Per dichiarare una struttura si usa la stessa sintassi della dichiarazione di una variabile, dove al posto
del tipo si inserisce il nome della struttura.
Nome_struttura MiaStruttura;
Viene definita, ora, una semplice struttura e un breve programma di esempio.
La struttura si chiama Studente e ha due membri di tipo string, che sono rispettivamente il no-
me e il cognome dello studente.
La variabile Alunno di tipo Studente si dichiara come segue.
Studente alunno;
Una volta dichiarata una variabile di tipo struct, si pu˜ accedere ai suoi membri
utilizzando la cosiddetta notazione puntata, che prevede che il nome della variabile,
per esempio alunno, sia seguito dal punto e dal nome della variabile membro a cui
si deve fare riferimento.
Nel caso dellÕassegnazione di un valore al membro nome della variabile alunno, lÕistruzione da uti-
lizzare • la seguente.
alunno.nome=ÓPippoÓ;
202
Unitˆ didattica 11 - Stringhe e strutture
Per assegnare, invece, il valore di una variabile membro a una variabile, si procede come di seguito.
string nomealunno = alunno.nome;
Una struttura deve essere dichiarata prima del main e dopo le direttive using del programma.
...........................................................................................................................................
Codice
Struttura1.cpp
1 #include <iostream>
2 #include <string>
3 using namespace std;
4
5 struct Studente
6 {
7 //membri della struttura
8 string nome;
9 string cognome;
10 };
11
12 //INIZIO
13 int main ()
14 {
15 //dichiara la variabile di tipo struct
16 Studente alunno;
17
18 //assegna i valori ai membri
19 //della struttura
20 alunno.nome="Paolo";
21 alunno.cognome="Rossi";
22
23 cout<<"\nNome dello studente: ";
24
25 //visualizza i membri della struttura
26 cout<<" "<<alunno.nome<<" "<<alunno.cognome;
27
28 //fine programma
29 cout << "\n\nFine ";
30 system ("pause");
31 return 0;
32 }
Prova di esecuzione
203
Sezione 4 - Strutture dei dati
1 1.7 M e t o d i c o s t r u t t o ri
Una struttura pu˜ contenere tutti i tipi di dati e una particolare funzione.
Esiste una funzione che pu˜ essere definita allÕinterno di una struttura e che ha la
particolare caratteristica di impostare lo stato iniziale della struttura. Tale tipo di
funzione viene definita costruttore, e il suo compito • inizializzare i membri della
struttura.
é da premettere che i membri di una struttura non possono essere inizializzati al momento della defi-
nizione. Per esempio, la seguente struttura:
genera un errore in fase di compilazione quindi, per avere la sicurezza che i membri siano ini-
zializzati, bisogna richiamare il costruttore al momento della creazione di una variabile di tipo
struttura.
Il costruttore deve avere obbligatoriamente lo stesso nome della struttura e avere tanti parametri (pas-
sati per valore) quanti sono i membri della struttura.
//costruttore
public Studente(string nome, string cognome)
{
//assegnazione dei parametri
204
Unitˆ didattica 11 - Stringhe e strutture
...........................................................................................................................................
Questo per quanto riguarda la definizione del costruttore allÕinterno della struttura; ora lo si deve
utilizzare al momento della creazione della struttura allÕinterno del codice.
Per la creazione dellÕoggetto alunno si deve utilizzare lÕistruzione riportata di seguito.
...
Studente Alunno(ÒPippoÓ,ÓInzaghiÓ);
...
In questo modo si crea un nuovo oggetto di nome alunno e si richiama la sua funzione costrutto-
re, che ne inizializza i membri.
Codice
Struttura2.cpp
1 #include <iostream>
2 #include <string>
3 using namespace std;
4
5 struct Studente
6 {
7 //membri della struttura
8 string nome;
9 string cognome;
10
11 //costruttore
12 Studente(string n, string c)
13 {
14 nome=n;
15 cognome=c;
16 }
17 };
18
19 //INIZIO
20 int main ()
21 {
22 //creazione della variabile struct alunno
23 Studente alunno("Paolo","Rossi");
24
25 cout<<"\nNome dello studente: "<<alunno.nome<<" "<<alunno.cognome;
26
27 //fine programma
28 cout << "\n\nFine ";
205
Sezione 4 - Strutture dei dati
29 system ("pause");
30 return 0;
31 }
Lo studio delle strutture introduce alcuni concetti importantissimi che sono alla base delle classi. In-
fatti, le strutture sono molto simili alle classi tranne per il fatto che non possono ereditare membri da
altre strutture. LÕereditarietˆ • un sistema che consente di espandere notevolmente le Òfunzionalitˆ
di una classeÓ ed • uno dei principi su cui si basa lÕintero linguaggio C++.
I concetti e gli argomenti che riguardano le classi verranno affrontati in maniera dettagliata nella
prossima Sezione.
206
Unità didattica 11 - Stringhe e strutture
Esercizi
Unità didattica 11
1 Scrivere un programma che acquisisca il nome dell’utente da tastiera e ne visualizzi ogni lettera su una
riga differente.
2 Scrivere un programma che acquisisca il nome dell’utente da tastiera e visualizzi le lettere in ordine in-
verso.
4 Scrivere un programma che contenga una funzione che abbia come parametro una stringa inserita dal-
l’utente e ne sostituisca tutte le occorrenze della lettera “i” con un “°”.
5 Come si può ricavare il primo carattere di una stringa? E l’ultimo? Come si elimina il primo carattere?
E l’ultimo?
Qual è il contenuto delle variabili nome, cognome ed età se l’utente digita gli input indicati di seguito?
James Carter
56
Lyndon Johnson
49
Hodding Carter 3rd
44
Richard M. Nixon
62
7 Quali sono i valori delle espressioni seguenti? In ciascuna riga si consideri che:
string s = “Hello” ;
string t = “World” ;
A s.substr(l, 2)
B s.length() + t.length()
* *
* *
*****
* *
* *
207
Sezione 4 - Strutture dei dati
Esercizi
Unità didattica 11
Questa lettera può essere definita come una costante stringa impostata come segue:
const string LETTER_H =
“* *\n* *\n*****\n* *\n* *\n”;
Si può effettuare la stessa operazione con le lettere E, L e O. Scrivere poi il messaggio disegnando le
lettere appena definite come stringhe.
H
E
L
L
O
9 Scrivere un programma che trasformi i nomi dei mesi nei numeri corrispondenti.
qs Tra le coppie di stringhe seguenti, quale viene prima secondo l’ordine lessicografico?
A “Tom”, “Dick”
B “Tom”, “Tornato”
C “church”, “Churchill”
D “operaio”, “operatore”
E “capo”, “capello”
F “C++”, “Car”
G “Tom”, “Tom”
H “cantuccini”, “canto”
I “tarallo”, “taralluccio”
qd Scrivere un programma che stampa la domanda “Vuoi continuare?” e legge un input dall’utente. Se l’in-
put dell’utente è “s”, “S”, “Sì”, “sì”, “Si”, “si”, “OK”, “Ok”, “ok”, “Sicuramente” o “Perché no? “, vi-
sualizzare “OK.” Se l’input dell’utente è “N” o “No”, visualizzare “Fine lavoro”. Altrimenti visualizzare
“Errore”.
qf Descrivere una struttura che possa contenere i dati relativi a una partita di calcio.
qg Descrivere una struttura che possa contenere i dati relativi ai voti delle verifiche sia orali che scritte di
un studente.
208
Sezione 5
Classi e oggetti
†
Obiettivi ◊ Risolvere un problema individuando gli oggetti
e le loro interazioni
◊ Definire una classe attraverso i suoi dati
e i suoi metodi
◊ Realizzare classi flessibili attraverso il polimorfismo
&
◊ Strutturare gerarchie di classi sfruttando lÕerditarietˆ
12.2 Incapsulazione
In un linguaggio a oggetti, il codice e i dati possono essere raggruppati in modo da creare una sorta
di Òscatola neraÓ. Quando il codice e i dati vengono raggruppati in questo modo, si crea un ogget-
to. In altre parole, un oggetto • un ÒdispositivoÓ che supporta lÕincapsulazione.
AllÕinterno di un oggetto, il codice, i dati o entrambi possono essere definiti privati per tale oggetto
oppure pubblici. Il codice o i dati privati sono noti e accessibili solo da parte degli elementi dellÕog-
getto stesso. Questo significa che il codice e i dati privati non risultano accessibili da parte di elementi
del programma che si trovano allÕesterno dellÕoggetto. Se il codice o i dati sono pubblici, risulteran-
no accessibili anche da altre parti del programma che non sono definite allÕinterno dellÕoggetto. Ge-
neralmente, le parti pubbliche di un oggetto sono utilizzate per fornire unÕinterfaccia controllata agli
elementi privati dellÕoggetto stesso. Un oggetto •, in tutto e per tutto, una variabile di un tipo de-
finito dallÕutente. Pu˜ sembrare strano pensare a un oggetto, che contiene codice e dati, come a
una variabile; tuttavia, nella programmazione a oggetti avviene proprio questo. Ogni volta che si
definisce un nuovo tipo di oggetto, si crea implicitamente un nuovo tipo di dato. Ogni specifica
istanza di questo tipo • una variabile composta.
211
Sezione 5 - Classi e oggetti
Si pu˜ assegnare alla classe indicata dal problema il nome di Rettangolo. I dati essenziali di un ret-
tangolo sono le misure della base e dellÕaltezza: base e altezza sono quindi i dati che necessaria-
mente fanno parte della classe Rettangolo. Inoltre, • interessante conoscere di un rettangolo lÕa-
rea e la misura del perimetro, quindi, alla classe Rettangolo associamo anche due metodi: uno per
calcolare lÕarea e lÕaltro per calcolare il perimetro. Pertanto fanno parte della classe Rettangolo
quattro membri:
base
altezza dati
area
perimetro metodi o funzioni
...........................................................................................................................................
12.3 Polimorfismo
I linguaggi di programmazione a oggetti supportano il polimorfismo, che • caratterizzato dalla fra-
se ÒunÕinterfaccia, pi• metodiÓ. In altri termini, vale lÕaffermazione riportata di seguito.
Esempio Termostato...........................................................................................................
Un esempio di polimorfismo tratto dal mondo reale • il termostato.
Una caratteristica tipica di tutti i termostati • che non serve conoscere il tipo di combustibile uti-
lizzato (gas, petrolio, elettricitˆ e cos“ via): il termostato funziona sempre nello stesso modo. In
questo caso, il termostato (che • lÕinterfaccia) • sempre lo stesso qualsiasi sia il tipo di combusti-
bile (metodo) utilizzato. Per esempio, se si desidera raggiungere una temperatura di 20 gradi, si
imposta il termostato a 20 gradi. Non • necessario sapere quale sia il meccanismo che fornisce il
calore.
...........................................................................................................................................
Questo stesso principio si pu˜ applicare anche in programmazione: per esempio, un programma po-
trebbe definire tre diversi tipi di array: un array per i valori interi, uno per i caratteri e uno per valori
in virgola mobile. Grazie al polimorfismo, • possibile creare solo due metodi (carica() e scri-
vi()), utilizzabili per i tre tipi di array.
Nel programma vengono create tre diverse versioni di questi metodi, una per ogni tipo di array, ma
il nome delle funzioni rimane lo stesso. Il compilatore seleziona automaticamente la funzione cor-
retta sulla base del tipo dei dati memorizzati, pertanto lÕinterfaccia dellÕarray (ovvero le funzioni ca-
rica() e scrivi()) rimane invariata, qualunque sia il tipo di array utilizzato. Naturalmente le sin-
gole versioni di queste funzioni definiscono implementazioni (metodi) specifiche per ciascun tipo di
dati. Il polimorfismo aiuta a ridurre la complessitˆ del programma consentendo di utilizzare la stes-
sa interfaccia per accedere a una categoria generale di azioni. é compito del compilatore selezionare
lÕazione specifica (ovvero il metodo) da applicare in una determinata situazione. Il programmatore
non deve pi• fare questa selezione in modo specifico, ma deve semplicemente ricordare e utilizzare
lÕinterfaccia generale.
212
Unità didattica 12 - Concetti generali
12.4 Ereditarietà
L’ereditarietà è il processo grazie al quale un oggetto acquisisce le proprietà di un altro oggetto. Questo
è un concetto fondamentale poiché chiama in causa il concetto di classificazione. Se si prova a riflettere,
la maggior parte della conoscenza è resa più gestibile da classificazioni gerarchiche. Per esempio, una me-
la rossa Delicious appartiene alla classificazione “mela”, che a sua volta appartiene alla classe “frutta”, che
a sua volta si trova nella classe più estesa “cibo”. Senza l’uso della classificazione, ogni oggetto dovrebbe
essere definito esplicitamente con tutte le proprie caratteristiche. L’uso della classificazione consente di
definire un oggetto sulla base delle qualità che lo rendono unico all’interno della propria classe.
È il meccanismo di ereditarietà a rendere possibile per un oggetto essere una specifica istanza di un caso
più generale. Come si vedrà, l’ereditarietà è un importante aspetto della programmazione a oggetti.
Automobile Autocarro
marca marca
velocità velocità
colore simbolo colore
numero porte per l’ereditarietà numero porte
livello carburante livello carburante
cilindrata cilindrata
parti tara
accelera portata massima
fermati parti
fai il pieno accelera
fermati
fai il pieno
carica
scarica
213
Sezione 5 - Classi e oggetti
La classe che è stata derivata da un’altra usando l’ereditarietà viene detta sottoclas-
se. La classe generatrice di una sottoclasse prende il nome di sopraclasse.
La relazione di ereditarietà permette di individuare una gerarchia di classi che può essere descritta
graficamente usando un grafo ad albero. Esso è costituito da un grafo orientato i cui nodi sono rap-
presentati dalle classi, o meglio dai diagrammi delle classi, e gli archi individuano la presenza di una
relazione di ereditarietà.
Mezzi di trasporto
Nell’esempio, la classe Moto è sottoclasse della classe dei veicoli A motore che a sua volta è sotto-
classe di Mezzi di trasporto.
In alto ci sono le classi più generiche che diventano più specializzate man mano si scende lungo la ge-
rarchia.
Nel diagramma sono state indicate le classi solo attraverso il loro nome, tralasciando l’elenco degli at-
tributi e dei metodi.
...........................................................................................................................................
La sottoclasse eredita dalla sopraclasse tutti gli attributi e tutti i metodi, evitando di ripetere la de-
scrizione degli elementi comuni nelle sottoclassi.
L’ereditarietà consente di condividere le similarità tra le classi e di inserire le differenze.
La nuova classe si differenzia dalla sopraclasse in due modi:
: per estensione, quando la sottoclasse aggiunge nuovi attributi e metodi che si sommano a
quelli ereditati, come nell’esempio della sottoclasse Autocarro che arricchisce di nuovi
membri la sopraclasse Auto;
: per ridefinizione, quando la sottoclasse ridefinisce i metodi ereditati. In pratica, viene data
un’implementazione diversa di un metodo. Si crea un nuovo metodo che ha lo stesso nome
del metodo ereditato da una sopraclasse, ma con funzionalità diversa. Quando per un ogget-
to della sottoclasse viene invocato un metodo ridefinito, viene eseguito il nuovo codice e non
quello che era stato ereditato.
214
Unitˆ didattica 12 - Concetti generali
Una classe • una categoria o un gruppo di oggetti (con questo termine includiamo,
per comoditˆ, anche gli esseri viventi) che hanno attributi simili e comportamenti
analoghi.
In C++ per creare un oggetto si deve innanzitutto definire la sua forma generale utilizzando la paro-
la chiave class. Una classe ha una sintassi simile a una struttura e pu˜ contenere al suo interno ol-
tre che dati anche parte di codice (incapsulamento). Ecco un esempio.
Esempio ClasseRettangolo................................................................................................
Definire la classe Rettangolo che contiene al suo interno i dati relativi alla base b e al-
lÕaltezza h e che racchiude al suo interno anche i due metodi utilizzati per il calcolo del-
lÕarea e del perimetro.
Codice
classeRettangolo.cpp
1 #include <iostream>
2 using namespace std;
3
4 //definisci la classe
5 class Rettangolo
6 {
7 public:
8 //definisci i dati membro della classe
9 float b, h;
10
11 //definisci i due metodi
12 float area() //calcola lÕarea
13 {
14 return b*h;
15 }
16 float perimetro() //calcola il perimetro
17 {
18 return 2*(b+h);
19 }
20 };
215
Sezione 5 - Classi e oggetti
Una classe pu˜ contenere parti private e pubbliche. In generale, tutti i membri definiti allÕinterno di
una classe sono privati.
I dati e i metodi private non sono visibili da alcunÕaltra funzione che non sia defini-
ta allÕinterno della classe. In assenza di indicazione (per default) si assume che il li-
vello di protezione sia private.
Questo • uno dei modi in cui si ottiene lÕincapsulazione: lÕaccesso a determinati membri pu˜ essere
controllato in modo rigido mantenendoli private.
NellÕesempio precedente le variabili b e h possono essere utilizzate solo dai metodi definiti allÕinter-
no della classe. Al contrario, i metodi area() e perimetro() possono essere utilizzati in qualsia-
si istruzione posta allÕesterno della definizione della classe.
Anche se questo non viene illustrato dallÕesempio presentato, • possibile definire funzioni private
che possono essere richiamate solo dai membri della classe.
Per rendere pubblica (ovvero accessibile da altre parti del programma) una parte della classe, • ne-
cessario dichiararla esplicitamente come pubblica utilizzando la parola chiave public. Tutte le va-
riabili e le funzioni definite dopo public possono essere utilizzate da tutte le altre funzioni del pro-
gramma. Essenzialmente, la parte rimanente del programma accede a un oggetto utilizzando le sue
funzioni pubbliche.
Anche se • possibile avere variabili pubbliche, si deve in generale cercare di limitarne lÕuso. Quindi si de-
ve cercare di rendere tutti i dati privati e controllare lÕaccesso ai dati utilizzando funzioni pubbliche.
Si ricordi che una classe racchiude metodi e dati. Solo i metodi di una classe hanno accesso ai dati
privati della classe in cui sono dichiarati. Perci˜ solo area() e perimetro() possono accedere
a b e h.
Tutti gli elementi che compongono la definizione di una classe prendono il nome di
membri.
AllÕinterno dei membri si distinguono i dati membro e le funzioni membro.
Spesso, in luogo di dati membro e di funzioni membro vengono usati i termini pi• immediati di dati e
metodi: anche nel seguito della trattazione verrˆ utilizzata prevalentemente questÕultima dizione.
Per la documentazione delle applicazioni informatiche pu˜ essere usato lo standard UML (Uni-
fied Modeling Language, linguaggio unificato di modellazione). In tale standard si usano i termi-
ni attributi e operazioni corrispondenti, rispettivamente, a dati membro e funzioni membro. Fa-
cendo riferimento alla classe Rettangolo vista nellÕesempio precedente, si pu˜ realizzare lo
schema seguente.
216
Unitˆ didattica 12 - Concetti generali
Lo standard UML definisce anche le modalitˆ per rappresentare graficamente una classe. Una classe
viene rappresentata da un rettangolo. Il nome della classe, per convenzione, • una parola con lÕini-
ziale maiuscola e appare alla sommitˆ del rettangolo. Se il nome della classe consiste di una parola
composta, a sua volta, da pi• parole, allora viene utilizzata la notazione in cui tutte le iniziali di ogni
parola sono scritte in maiuscolo.
NomeClasse
Dopo il nome della classe vengono descritti i dati a essa appartenenti. Un dato rappresenta una pro-
prietˆ di una classe; esso descrive un insieme di valori che la proprietˆ pu˜ avere. Una classe pu˜ ave-
re zero o pi• dati. La lista dei dati di una classe viene separata graficamente dal nome della classe a cui
appartiene tramite una linea orizzontale.
Un dato il cui nome • costituito da una sola parola viene scritto sempre in caratteri minuscoli. Se, in-
vece, il nome del dato consiste di pi• parole (per esempio, informazioniCliente) allora il no-
me del dato viene scritto unendo tutte le parole che ne costituiscono il nome stesso con la particola-
ritˆ che la prima parola viene scritta in minuscolo mentre le successive hanno la loro prima lettera in
maiuscolo.
NomeClasse
tipo1 dato1
tipo2 dato2 = Òvalore defaultÓ
ÉÉ
Come si vede nella figura precedente, • possibile specificare un tipo per ogni dato (string, int,
bool ecc.). é anche possibile specificare il valore di default che un dato pu˜ avere.
Un metodo • unÕazione che gli oggetti di una certa classe possono compiere. Analogamente al nome
degli attributi, il nome di un metodo viene scritto con caratteri minuscoli. Anche qui, se il nome del-
lÕoperazione consiste di pi• parole, allora tali parole vengono unite tra di loro e ognuna di esse, eccet-
to la prima, viene scritta con il primo carattere maiuscolo. La lista dei metodi viene rappresentata gra-
ficamente sotto la lista degli attributi e separata da questa tramite una linea orizzontale.
NomeClasse
tipo1 dato1
tipo2 dato2 = Òvalore defaultÓ
ÉÉ
tipo1 metodo1()
tipo2 metodo2 (lista parametri)
Anche i metodi possono avere informazioni addizionali. Nelle parentesi che seguono il nome di un
metodo, infatti, • possibile mostrare gli eventuali parametri necessari al metodo insieme al loro tipo.
Infine, se il metodo rappresenta una funzione • necessario anche specificare il tipo restituito.
217
Sezione 5 - Classi e oggetti
Rettangolo rett;
int numero;
In questo caso, rett • unÕistanza di Rettangolo. é anche possibile creare oggetti nel luogo stes-
so in cui viene definita la classe, specificandone il nome dopo la parentesi graffa di chiusura, esatta-
mente come avviene nel caso delle strutture.
Per ricapitolare: in C++ class crea un nuovo tipo di dati che pu˜ essere utilizzato per
creare oggetti appartenenti alla classe.
Pertanto, un oggetto • unÕistanza di una classe esattamente come altre variabili sono istanze, per
esempio, del tipo int. In altre parole, una classe • unÕastrazione logica, mentre un oggetto • reale
(ovvero esiste allÕinterno della memoria del computer).
Quando si fa riferimento a un membro di una classe da una parte di codice che non si trova allÕinter-
no della classe stessa, lÕoperazione deve essere eseguita sempre in congiunzione con un oggetto di ta-
le classe. A tale scopo, si deve utilizzare il nome dellÕoggetto seguito dallÕoperatore punto seguito a
sua volta dal membro.
Questa regola si applica quando si deve accedere sia a dati membro sia a funzioni membro. Per esem-
pio, il frammento di codice seguente richiama il metodo area() per lÕoggetto rett.
AllÕinterno di una classe, un metodo pu˜ richiamare un altro metodo oppure pu˜ fare riferimento di-
rettamente ai dati senza utilizzare lÕoperatore punto (.).
Il nome dellÕoggetto e lÕoperatore punto (.) devono essere utilizzati solo quando lÕaccesso a un
membro avviene da parte di codice che non appartiene alla classe.
218
Unitˆ didattica 12 - Concetti generali
Rettangolo2.cpp
1 #include <iostream>
2 using namespace std;
3
4 //definisci la classe
5 class Rettangolo
6 {
7 public:
8 //definisci i dati membro della classe
9 float b, h;
10
11 //definisci i due metodi
12 float area() //calcola lÕarea
13 {
14 return b*h;
15 }
16 float perimetro() //calcola il perimetro
17 {
18 return 2*(b+h);
19 }
20 };
21
22 //INIZIO
23 int main ()
24 {
25 //definisci lÕoggetto rett
26 Rettangolo rett;
27
28 //inizializza le variabili b, h
29 rett.b = 10;
30 rett.h = 6;
31
32 //scrivi lÕarea e il perimetro invocando i relativi metodi
33 cout << "\nArea del rettangolo = " << rett.area();
34 cout << "\nPerimetro rettangolo = " << rett.perimetro();
35
36 //fine programma
37 cout << "\n\nFine ";
38 system ("pause");
39 return 0;
40 }
Prova di esecuzione
219
Sezione 5 - Classi e oggetti
ulteriori precisazioni di tipo teorico, i dati membro hanno livello di protezione public, anche se ta-
le scelta risulta decisamente sconsigliabile.
Alle righe 12 e 16 sono definiti i metodi legati alla classe Rettangolo.
Alla riga 20 la parentesi graffa chiusa e il punto e virgola indicano la fine della definizione della
classe.
Alla riga 26 viene dichiarato lÕoggetto rett che appartiene alla classe Rettangolo.
Alle righe 29 e 30 • indicata lÕinizializzazione delle variabili b e h che fanno parte dellÕoggetto rett.
Alle righe 33 e 34, allÕinterno delle operazioni di scrittura a video, • presente lÕinvocazione dei me-
todi area() e perimetro().
La scelta di assegnare ai dati membro b e h il livello di protezione public • da sconsigliare: infatti i da-
ti membro di una classe non devono essere disponibili per le parti del programma esterne alla definizio-
ne della classe, vale a dire devono avere livello di protezione private. In questo modo si garantisce che
solo i metodi definiti allÕinterno della classe possono intervenire sui dati della classe stessa.
...........................................................................................................................................
Viene presentato ora un esempio dove i dati della classe vengono definiti private.
Rettangolo3.cpp
1 #include <iostream>
2 using namespace std;
3
4 //definisci la classe
5 class Rettangolo
6 {
7 //definisci i dati membro con protezione private
8 float b, h;
9
10 public:
11 //definisci i due metodi
12 float area() // calcola lÕarea
13 {
14 return b*h;
15 }
16 float perimetro() // calcola il perimetro
17 {
18 return 2*(b+h);
19 }
20
21 //leggi base e altezza da console
22 void leggi()
23 {
24 //leggi base
220
Unitˆ didattica 12 - Concetti generali
221
Sezione 5 - Classi e oggetti
Esercizi
Unità didattica 12
8 Definire la classe punto del piano cartesiano con i metodi sposta_a_destra, sposta_a_sini-
stra, sposta_in_alto e sposta_in_basso.
9 Definire la classe verifica con i dati materia, data e voto e con il metodo cambiaVoto. Disegna il
diagramma della classe e realizza in C++ il programma che utilizza tale classe.
qa Definire la classe NumeroReale con i metodi che restituiscono la parte intera e la parte decimale.
qs Definire la classe Approssimazione con il dato valoreIniziale e con tre metodi:
A perDifetto
B perEccesso
C interoPiuVicino
qd Definire la classe Segmento individuata dalle coordinate sul piano cartesiano dei suoi vertici e che pos-
siede i metodi per restituire la lunghezza del segmento e le coordinate del punto medio.
Indicare con un diagramma ad albero che sfrutta l’ereditarietà la gerarchia delle classi per le seguen-
ti categorie:
qf Periferiche di un calcolatore;
qg Elettrodomestici;
qh Specie animali;
qj Sport;
qk Poligoni.
ql Per ciascuno delle gerarchie indicate negli esercizi dal numero 14 al numero 18, individuare almeno un
dato e un metodo comune a tutte le classi.
222
Unitˆ didattica
13
Polimorfismo
ed ereditarietˆ
CHE COSA IMPARERAI A FARE
Un costruttore • un particolare metodo di una classe: porta lo stesso nome della clas-
se e permette lÕinizializzazione automatica dellÕoggetto su cui si intende operare.
Esempio Costruttore...........................................................................................................
Con riferimento al codice dellÕesempio Rettangolo2 dellÕUnitˆ didattica 12, modificare il
modulo leggi() in modo da farlo rientrare come costruttore nella classe Rettangolo.
Codice
Costruttore.cpp
1 #include <iostream>
2 using namespace std;
3
4 //definisci la classe
5 class Rettangolo
6 {
7 //definisci i dati membro con protezione private
8 float b, h;
9
10 public:
11 //definisci i due metodi
12 float area() //calcola lÕarea
13 {
14 return b*h;
15 }
16 float perimetro() //calcola il perimetro
17 {
18 return 2*(b+h);
19 }
20 //COSTRUTTORE
21 //inizializza base e altezza leggendo dati da console
22 public: Rettangolo()
23 {
24 //leggi base
25 cout << "\nInserisci la base => ";
224
Unità didattica 13 - Polimorfismo ed ereditarietà
26 cin >> b;
27
28 //leggi altezza
29 cout << "\nInserisci altezza => ";
30 cin >> h;
31
32 return;
33 }
34 };
Nel caso specifico dell’esempio precedente, il costruttore ha il compito di acquisire dati da tastiera,
ma nella pratica la maggior parte dei costruttori non ha bisogno di input né di output: semplice-
mente, il metodo si occupa di eseguire varie inizializzazioni.
Il costruttore di un oggetto viene richiamato automaticamente nel momento in cui deve essere crea-
to l’oggetto.
Questo significa che viene richiamato al momento della dichiarazione dell’oggetto. Se si è abituati a
pensare che una dichiarazione sia un’istruzione passiva, occorre prepararsi a cambiare idea: in C++,
una dichiarazione è un’istruzione che viene eseguita come qualunque altra. La distinzione non è pu-
ramente accademica: il codice eseguito per costruire un oggetto può essere anche molto pesante,
pertanto, il costruttore di un oggetto viene richiamato una sola volta per ogni oggetto globale; nel
caso di oggetti locali, il costruttore viene richiamato ogni volta che si incontra la dichiarazione di un
nuovo oggetto.
In molte circostanze, un oggetto deve eseguire una o più azioni nel momento in cui finisce la pro-
pria esistenza.
Gli oggetti locali vengono costruiti nel momento in cui si entra nel blocco ove si trovano e vengono
distrutti all’uscita dal blocco; gli oggetti globali vengono distrutti nel momento in cui termina il pro-
gramma. Quando viene distrutto un oggetto, viene automaticamente richiamato il relativo distrut-
tore (se presente).
Vi sono molti casi in cui è necessario utilizzare un distruttore. Per esempio, potrebbe essere neces-
sario de-allocare la memoria precedentemente allocata dall’oggetto oppure potrebbe essere necessa-
rio chiudere un file aperto. In C++ è il distruttore a gestire gli eventi di disattivazione.
225
Sezione 5 - Classi e oggetti
Codice
Rettangolo4.cpp
1 #include <iostream>
2 using namespace std;
3
4 //definisci la classe
5 class Rettangolo
6 {
7 //definisci i dati membro con protezione private
8 float b, h;
9
10 public:
11 //definisci i due metodi
12 float area() //calcola lÕarea
13 {
14 return b*h;
15 }
16 float perimetro() //calcola il perimetro
17 {
18 return 2*(b+h);
19 }
20 //COSTRUTTORE
21 //inizializza base e altezza leggendo dati da console
22 public: Rettangolo()
23 {
24 //leggi base
25 cout << "\nInserisci la base => ";
26 cin >> b;
27
28 //leggi altezza
29 cout << "\nInserisci altezza => ";
30 cin >> h;
31
32 //segnala costruzione completa
33 cout << "\nCostruzione rett \n ";
34
35 return;
36 }
37
38 //DISTRUTTORE
39 ~Rettangolo()
40 {
41 //segnala distruzione
42 cout << "\n\nDistruzione rett \n";
43 }
44 };
45
46 //INIZIO
47 int main ()
48 {
49 //definisci lÕoggetto rett
50 Rettangolo rett;
51
52 //scrivi lÕarea e il perimetro invocando i relativi metodi
226
Unitˆ didattica 13 - Polimorfismo ed ereditarietˆ
Prova di esecuzione
Esempio Persona................................................................................................................
Dichiarare una classe di nome Persona con i dati cognome e nome e con due metodi: uno
per scrivere prima il cognome poi il nome, lÕaltro per scrivere prima il nome poi il cognome.
Utilizzare un costruttore parametrizzato che consegni a un oggetto della classe i dati ac-
quisiti da tastiera.
227
Sezione 5 - Classi e oggetti
Diagramma di classe
Persona
nome
cognome
cognomeNome()
nomeCognome()
Codice
Persona.cpp
1 #include <iostream>
2 #include <string>
3 using namespace std;
4
5 class Persona
6 {
7 //definisci i dati membro della classe con protezione private
8 string n; //Nome
9 string c; //Cognome
10 string tutto;
11
12 //concatena cognome e nome
13 public: string cognomeNome()
14 {
15 tutto = c+" "+n;
16 return tutto;
17 }
18
19 //concatena nome e cognome
20 public: string nomeCognome()
21 {
22 tutto = n+" "+c;
23 return tutto;
24 }
25
26 //COSTRUTTORE : inizializza c e n
27 public: Persona(string co, string no)
28 {
29 c = co;
30 n = no;
31 }
32 };
33
34 //INIZIO
35 int main ()
36 {
37 //dati di input
228
Unitˆ didattica 13 - Polimorfismo ed ereditarietˆ
38 string cognome;
39 string nome;
40
41 //leggi il cognome
42 cout << "\nInserisci cognome => ";
43 cin >> cognome;
44
45 //leggi nome
46 cout << "\nInserisci nome => ";
47 cin >> nome;
48
49 //dichiara l'oggetto amico di classe Persona
50 Persona amico(cognome,nome);
51
52 //scrivi prima il cognome e poi il nome
53 cout << "\nCognome e nome = " << amico.cognomeNome();
54
55 //scrivi prima il nome e poi il cognome
56 cout << "\nNome e cognome = " << amico.nomeCognome();
57
58 //fine programma
59 cout << "\n\nFine ";
60 system ("pause");
61 return 0;
62 }
Prova di esecuzione
229
Sezione 5 - Classi e oggetti
I costruttori parametrizzati sono molto utili poichŽ evitano di dover eseguire una nuova chiamata di un
metodo semplicemente per inizializzare una o pi• variabili in un oggetto: ogni chiamata di un metodo evi-
tata rende il programma pi• efficiente.
A differenza dei comuni dati membro, non viene creata una singola copia di una variabile membro
static per ogni oggetto.
Indipendentemente dal numero di oggetti creati per una classe, esiste una sola copia dei dati mem-
bro static. Pertanto, tutti gli oggetti di tale classe utilizzano la stessa variabile. Tutte le variabi-
li static vengono inizializzate a zero nel momento in cui viene creato il primo oggetto.
Quando si dichiarano i dati membro static allÕinterno di una classe, non si definiscono tali da-
ti. Questo significa che non si sta allocando spazio di memoria per tali dati (in C++, una dichiara-
zione descrive qualcosa e una definizione crea qualcosa). Si deve pertanto fornire una definizione
globale per i dati membro static in un altro punto, allÕesterno della classe.
LÕutilizzo di variabili dichiarate static comporta anche lÕintroduzione di un nuovo operatore, che
permette di specificare il campo dÕazione di una variabile.
Prima che una variabile dichiarata static possa essere utilizzata • necessario specificare lÕambito a
cui la variabile appartiene, utilizzando la sintassi riportata di seguito.
NomeClasse :: variabile;
Per comprendere lÕuso e gli effetti dei dati membri static, si consideri il seguente esempio.
Esempio Static....................................................................................................................
Definire una classe Esempio con due dati membro interi: a e b, con a dichiarato static.
Dichiarare e costruire due oggetti con valori iniziali diversi.
Verificare il comportamento di a e di b.
Diagramma di classe
ClasseEsempio
static a
b
mostra
230
Unitˆ didattica 13 - Polimorfismo ed ereditarietˆ
Static.cpp
1 #include <iostream>
2 using namespace std;
3
4 //dichiara la classe ClasseEsempio
5 class ClasseEsempio
6 {
7 //dichiara i dati membro della ClasseEsempio
8 static int a;
9 int b;
10
11 //inizializza a e b
12 public:
13 void imposta (int i, int j)
14 {
15 a = i;
16 b = j;
17 }
18
19 //mostra su quale oggetto si sta lavorando
20 //e il contenuto delle variabili
21 public: void mostra(int numOggetto)
22 {
23 cout << "\nIn oggetto " << numOggetto;
24 cout << "\nvariabile statica a = " << a;
25 cout << "\nvariabile non st. b = " << b<<Ó\nÓ;
26 }
27 };
28
29 int ClasseEsempio :: a; //definisci a
30
31 //INIZIO
32 int main ()
33 {
34 //dichiara due oggetti della classe ClasseEsempio
35 ClasseEsempio oggetto1;
36 ClasseEsempio oggetto2;
37
38 //inizializza a e b
39 oggetto1.imposta(10,10);
40
41 //mostra il contenuto di oggetto1
42 oggetto1.mostra(1);
43
44 //inizializza a e b
45 oggetto2.imposta(20,20);
46
47 //mostra il contenuto di oggetto2
48 oggetto2.mostra(2);
49
50 //mostra il contenuto di oggetto1
51 oggetto1.mostra(1);
52
53 //fine programma
54 cout << "\n\nFine ";
231
Sezione 5 - Classi e oggetti
55 system ("pause");
56 return 0;
57 }
Prova di esecuzione
Un altro interessante uso di una variabile membro static consiste nel registrare il numero di og-
getti esistenti per una determinata classe.
Esempio Counter................................................................................................................
Definire pi• oggetti per ClasseEsempio e mostrarne il numero.
ClasseEsempio
a
b
ClasseEsempio()
mostra()
Codice
Counter.cpp
1 #include <iostream>
2 using namespace std;
3
4 //dichiara la classe Contatore
5 class Contatore
232
Unitˆ didattica 13 - Polimorfismo ed ereditarietˆ
6 {
7 //definisci i dati membro della classe Contatore
8 int a;
9 int b;
10 static int ctr; //contatore
11
12 //COSTRUTTORE, inizializza a e b
13 public:
14 Contatore (int i, int j)
15 {
16 a = i;
17 b = j;
18 ctr++;
19 }
20
21 //DISTRUTTORE
22 ~Contatore ()
23 {
24 ctr--;
25 }
26
27 //mostra numero degli oggetti
28 void mostra()
29 {
30 cout << "\nContatore oggetti = " << ctr;
31 }
32 };
33
34 int Contatore :: ctr; //definisci contatore
35
36 //INIZIO
37 int main ()
38 {
39 //dichiara un oggetto di classe Contatore
40 Contatore oggetto1(10,10);
41
42 //mostra numero oggetti
43 oggetto1.mostra();
44
45 //dichiara un oggetto di classe Contatore
46 Contatore oggetto2(20,20);
47
48 //mostra numero oggetti
49 oggetto1.mostra();
50
51 //distruggi oggetto2
52 oggetto2.~Contatore();
53
54 //mostra numero oggetti
55 oggetto1.mostra();
56
57 //fine programma
58 cout << "\n\nFine ";
59 system ("pause");
60 return 0;
61 }
233
Sezione 5 - Classi e oggetti
Prova di esecuzione
LÕimpiego di variabili membro static dovrebbe consentire di eliminare la necessitˆ di utilizzare variabi-
li globali. Il problema derivante dallÕuso di variabili globali in tecniche di programmazione a oggetti consi-
ste nel fatto che quasi sempre esse violano il principio di incapsulazione.
13.4 Overloading
Esaminiamo ora gli argomenti dellÕoverloading dei metodi e del polimorfismo.
LÕoverloading dei metodi • uno degli aspetti fondamentali del linguaggio di programmazione C++.
Infatti, esso non solo fornisce il supporto per il polimorfismo in fase di compilazione ma aggiunge al
linguaggio flessibilitˆ e comoditˆ. Tra i metodi modificati tramite overloading, quelli pi• importanti
sono i costruttori.
Il ÒsegretoÓ dellÕoverloading • il fatto che ogni ridefinizione del metodo deve utilizzare parametri di
tipo differente oppure in numero differente.
Grazie a queste diversitˆ, il compilatore • in grado di scegliere quale metodo utilizzare in una deter-
minata situazione.
234
Unitˆ didattica 13 - Polimorfismo ed ereditarietˆ
Codice
ValoreAss.cpp
1 #include <iostream>
2 using namespace std;
3
4 class ValoreAssoluto
5 {
6 //restituisci il valore assoluto di un intero
7 public:
8 int abs(int intero)
9 {
10 if (intero<0)
11 {
12 intero = -intero;
13 }
14 return intero;
15 }
16 //restituisci il valore assoluto di un double
17 double abs(double doppio)
18 {
19 if (doppio<0)
20 {
21 doppio = -doppio;
22 }
23 return doppio;
24 }
25
26 //restituisci il valore assoluto di un intero lungo
27 long abs(long lungo)
28 {
29 if (lungo<0)
30 {
31 lungo = -lungo;
32 }
33 return lungo;
34 }
35 };
36
37 //INIZIO
38 int main ()
39 {
40 //definisci l'oggetto calcola della classe ValoreAssoluto
41 ValoreAssoluto calcola;
42
43 //scrivi il valore assoluto di un intero
44 //richiamando il metodo abs di calcola
45 cout << "\n" << calcola.abs(-10);
46
47 //scrivi il valore assoluto di un double
48 //richiamando il metodo abs di calcola
49 cout << "\n" << calcola.abs(-11.1);
50
51 //scrivi il valore assoluto di un int lungo
52 //richiamando il metodo abs di calcola
235
Sezione 5 - Classi e oggetti
Prova di esecuzione
...........................................................................................................................................
Come si • detto, la caratteristica principale dellÕoverloading dei metodi • il fatto che questi devono
differire per quanto riguarda il tipo e/o il numero dei parametri.
Dunque due metodi non possono differire solo per il tipo di dato restituito. Per esempio, ecco un
modo errato per eseguire lÕoverloading: se per il metodo mioMetodo() si scrive:
int mioMetodo(int 1);
float mioMetodo(int 1);
si compie un errore perchŽ le due intestazioni differiscono solo per il tipo del valore restituito.
13.5 Polimorfismo
I costruttori possono essere modificati tramite overloading e, nella pratica comune, questo avvie-
ne molto spesso. I motivi principali che spingono a utilizzare il polimorfismo dei costruttori sono
la maggiore flessibilitˆ che pu˜ essere data alla definizione di una classe e la possibilitˆ di creare og-
getti tra loro simili appartenenti alla medesima classe.
Spesso si crea una classe per la quale esistono due o pi• modi per costruire un oggetto: in tal caso,
• opportuno fornire una funzione costruttore modificata tramite overloading per entrambi questi
metodi.
236
Unità didattica 13 - Polimorfismo ed ereditarietà
Questa è una regola fondamentale in quanto, se si cerca di creare un oggetto per il quale non esi-
ste un costruttore, viene prodotto un errore in fase di compilazione.
Offrendo un costruttore per ognuna delle modalità in cui un utilizzatore della classe può voler co-
struire un oggetto, si aumenta la flessibilità della classe.
L’utente è libero di scegliere il modo migliore per costruire un oggetto in una determinata circo-
stanza.
ax + by + c = 0
y = mx + q
Nel primo caso, la retta è individuata da tre parametri: a, b, c ; nel secondo, dai para-
metri m, q.
Definire la classe retta con due costruttori, uno per la forma esplicita e uno per la forma im-
plicita.
Aggiungere alla classe Retta i metodi:
: mostraImplicita() per scrivere l’equazione della retta in forma esplicita (si ricordi
che m = -a/b e che q = –c/b);
: mostraIntersezioni() che visualizza le coordinate dei punti di intersezione della
retta con gli assi cartesiani.
Retta
a, b, c // coefficienti retta in forma esplicita
m, q // coefficienti retta in forma implicita
Retta(a, b, c) //costruttore retta esplicita
Retta(m, q) //costruttore retta implicita
mostraImplicita()
mostraIntersezioni()
Codice
Retta.cpp
1 #include <iostream>
2 using namespace std;
3
4 //definisci la classe
5 class Retta
237
Sezione 5 - Classi e oggetti
6 {
7 double a, b, c; //coefficienti retta in forma esplicita
8 double m, q; //coefficienti retta in forma implicita
9
10 public:
11 //COSTRUTTORE della retta in forma esplicita
12 Retta(double c1, double c2, double c3)
13 {
14 a = c1;
15 b = c2;
16 c = c3;
17 m = -a/b;
18 q = -c/b;
19 }
20
21 //COSTRUTTORE della retta in forma implicita
22 Retta(double c1, double c2)
23 {
24 m = c1;
25 q = c2;
26 }
27
28 //scrivi la retta in forma implicita
29 void mostraImplicita()
30 {
31 cout << "\nY = " << m << "X + " << q << "\n";
32 }
33
34 //scrivi le coordinate delle intersezioni con gli assi cartesiani
35 void mostraIntersezioni()
36 {
37 cout << "\nIntersezioni asse X: (" << -q/m << "," << 0 << ")";
38 cout << "\nIntersezioni asse Y: (" << 0 << "," << q << ")";
39 }
40 };
41
42 //INIZIO
43 int main ()
44 {
45 double c1, c2, c3; //coefficienti della retta
46
47 //acquisisci i coefficienti della retta in forma esplicita
48 cout << "\nInserisci a ";
49 cin >> c1;
50 cout << "\nInserisci b ";
51 cin >> c2;
52 cout << "\nInserisci c ";
53 cin >> c3;
54
55 //dichiara l'oggetto esplicita della classe Retta
56 Retta esplicita(c1,c2,c3);
57
58 //invoca il metodo per visualizzare la retta
59 //anche in forma implicita
60 esplicita.mostraImplicita();
238
Unitˆ didattica 13 - Polimorfismo ed ereditarietˆ
61
62 //invoca il metodo per visualizzare le intersezioni con gli assi
63 esplicita.mostraIntersezioni();
64
65 //acquisisci i coefficienti della retta in forma esplicita
66 cout << "\n\nInserisci m ";
67 cin >> c1;
68 cout << "\nInserisci q ";
69 cin >> c2;
70
71 //definisci l'oggetto implicita della classe Retta
72 Retta implicita(c1,c2);
73
74 //invoca il metodo per visualizzare le intersezioni con gli assi
75 implicita.mostraIntersezioni();
76
77 //fine programma
78 cout << "\n\nFine ";
79 system ("pause");
80 return 0;
81 }
Prova di esecuzione
239
Sezione 5 - Classi e oggetti
metri (a, b, c); tale costruttore • in overload con il costruttore definito alla riga 22, il quale con-
tiene solo due parametri (m e q) ed • in alternativa con il costruttore precedente. Sarˆ il compila-
tore a scegliere il costruttore opportuno in base al numero dei parametri specificati al momento
della definizione di un oggetto appartenente alla classe. Alla riga 29 viene dichiarato il metodo mo-
straImplicita(), che scrive lÕequazione della retta. Alla riga 35 viene dichiarato il metodo
mostraIntersezioni(), che scrive le coordinate dei punti di intersezione della retta con gli
assi cartesiani. Alla riga 56 viene dichiarato e definito lÕoggetto esplicita che appartiene alla
classe Retta: il suo costruttore utilizza tre parametri. Alle righe 60 e 63 vengono invocati dal-
lÕoggetto esplicita i metodi mostraImplicita() e mostraIntersezioni(). Alla riga
72 viene dichiarato e definito lÕoggetto implicita che appartiene alla classe Retta: il suo co-
struttore utilizza due parametri. Alla riga 75 viene invocato dallÕoggetto implicita il metodo
mostraIntersezioni().
...........................................................................................................................................
NellÕesempio precedente lo scopo dellÕoverloading del costruttore di Retta • quello di rendere pi• fles-
sibile e semplice il suo uso. Tale maggiore flessibilitˆ e facilitˆ dÕuso • particolarmente importante quan-
do si creano librerie di classi che possono essere utilizzate anche da altri programmatori.
13.6 Ereditarietˆ
LÕereditarietˆ costituisce una delle pietre angolari della programmazione orientata agli oggetti, in
quanto consente di creare classificazioni gerarchiche.
Utilizzando lÕereditarietˆ, • possibile creare una classe generale che definisce le caratteristiche comu-
ni a una serie di oggetti correlati. Tale classe pu˜, in seguito, essere ereditata da una o pi• classi,
ognuna delle quali aggiunge alla classe ereditata solo elementi specifici.
Il supporto dellÕereditarietˆ del C++ • ricco e flessibile. Quando una classe ne eredita unÕaltra, i mem-
bri della classe base divengono anche membri della classe derivata.
Per definire una classe derivata da unÕaltra si utilizza la sintassi riportata di seguito.
class Nome-classe-derivata : Nome-classe-base
{
corpo_della_classe
}
240
Unitˆ didattica 13 - Polimorfismo ed ereditarietˆ
Ordine
descrizione
quantitˆ
prezzo
dataOrdine
indirizzoCliente
costruttore
showOrdine
Spedizione
dataSpedizione
showSpedizione
Codice
Ordine1.cpp
1 #include <iostream>
2 #include <string>
3 using namespace std;
4
5 //descrivi la classe Ordine
6 class Ordine
7 {
8 string descrizione;
9 int quantitˆ ;
10 double prezzo;
11 string dataOrdine;
12 string nomeCliente;
13
14 //metodo per la scrittura del contenuto di Ordine
15 public:
16 void showOrdine()
17 {
18 cout << "\nOrdine";
19 cout << "\nArticolo venduto " << descrizione;
241
Sezione 5 - Classi e oggetti
242
Unitˆ didattica 13 - Polimorfismo ed ereditarietˆ
Prova di esecuzione
243
Sezione 5 - Classi e oggetti
Alla riga 34 è definito il costruttore effettivo della classe Ordine: ha la funzione di assegnare un va-
lore ai dati che appartengono alla classe.
Alla riga 45 inizia la descrizione della classe Spedizione e, attraverso il carattere “:” viene specifi-
cato che Spedizione è derivata da Ordine.
Dalla riga 47 alla riga 59 è presente il corpo della classe Spedizione: è molto semplice, ma va ri-
cordato che i suoi membri costituiscono una estensione dei membri della classe Ordine.
Dalla riga 73 alla riga 77 si trovano le istruzioni necessarie all’acquisizione dei dati per istanziare, at-
traverso il costruttore della classe, un nuovo oggetto della classe Ordine.
Alla riga 80 viene creato l’oggetto ord appartenente alla classe Ordine.
Alla riga 92 viene creato l’oggetto sped appartenente a Spedizione.
Va sottolineato il fatto che la riga 92 del codice invoca implicitamente il costruttore Ordine(). Nella
classe Ordine sono definiti in overloading due costruttori; in par ticolare, quello che viene invocato al
momento della creazione dell’istanza della classe derivata è il costruttore che è privo di parametri.
La conseguenza di tutto ciò è che i dati membro di Ordine vengono inizializzati una seconda volta fa-
cendo in modo che i dati numerici risultino nulli e i dati stringa diventino vuoti. Nell’esempio prece-
dente non si nota questo fatto, ma risulta evidente quando si vogliono utilizzare i dati della classe ba-
se. Vedremo nel prossimo esempio come ovviare a quanto sopra.
Per semplicità e per ridurre le righe di codice, le date sono state definite come stringhe compiendo, co-
sì, una piccola scorrettezza di programmazione.
...........................................................................................................................................
Viene affrontata ora la seconda parte del problema della gestione degli ordini: quello di produrre i
dati relativi alla fattura che la società deve emettere a fronte dell’ordine spedito. Per tale problema, ci
limitiamo a calcolare il totale della fattura.
Fattura
totaleFattura
244
Unitˆ didattica 13 - Polimorfismo ed ereditarietˆ
Codice
Ordine2.cpp
1 #include <iostream>
2 #include <string>
3 using namespace std;
4
5 const int iva = 20;
6
7 //descrivi la classe Ordine
8 class Ordine
9 {
10 string descrizione;
11 static int quantitˆ ;
12 static double prezzo;
13 string dataOrdine;
14 string nomeCliente;
15
16 //metodo per la scrittura del contenuto di Ordine
17 public:
18 void showOrdine()
19 {
20 cout << "\nOrdine";
21 cout << "\nArticolo venduto " << descrizione;
22 cout << "\nQuantitˆ venduta " << quantitˆ ;
23 cout << "\nPrezzo di vendita " << prezzo;
24 cout << "\nData ordine " << dataOrdine;
25 cout << "\nImporto " << quantitˆ *prezzo;
26 cout << "\nCliente " << nomeCliente;
27 }
28
29 //costruttore indispensabile per la derivazione
30 Ordine()
31 {
32 //metodo vuoto e senza parametri
33 }
34
35 //costruttore effettivo per l'impostazione dei dati
36 Ordine(string d, int q, double p, string dO, string nC)
37 {
38 descrizione = d;
39 quantitˆ = q;
40 prezzo = p;
41 dataOrdine = dO;
42 nomeCliente = nC;
43 }
44
45 //restituisci l'importo dell'ordine
46 double getImporto()
47 {
48 return quantitˆ *prezzo;
49 }
50 };
51
245
Sezione 5 - Classi e oggetti
246
Unitˆ didattica 13 - Polimorfismo ed ereditarietˆ
106 cout << "\nInserisci nome cliente "; cin >> nC;
107
108 //Dichiara e definisci l'oggeto ord
109 Ordine ord (d, q, p, dO, nC);
110
111 //scrivi il contenuto di ord
112 ord.showOrdine();
113
114 string dS; //data di spedizione
115
116 //acquisisci data di spedizione
117 cout << "\n\nInserisci data spedizione "; cin >> dS;
118
119 //dichiara e definisci l'oggetto sped
120 //derivato da ord
121 Spedizione sped(dS);
122
123 //scrivi il contenuto di sped
124 sped.showSpedizione();
125
126 //dichiara e definisci l'oggetto fattura
127 //derivato da sped
128 Fattura fatt;
129
130 //scrivi il totale della fattura
131 //invocando il metodo totaleFattura
132 cout << "\nTotale fattura = " << fatt.totaleFatt();
133
134 //fine programma
135 cout << "\n\nFine ";
136 system ("pause");
137 return 0;
138 }
Prova di esecuzione
247
Sezione 5 - Classi e oggetti
248
Unità didattica 13 - Polimorfismo ed ereditarietà
Esercizi
Unità didattica 13
Per gli esercizi da 1 a 4 definire il costruttore.
1 Definire la classe punto del piano cartesiano con i metodi sposta_a_destra, sposta_a_sinistra,
sposta_in_alto e sposta_in_basso.
2 Definire la classe verifica con i dati materia, data e voto e con il metodo cambiaVoto. Disegna
il diagramma della classe e realizza in C++ il programma che utilizza tale classe.
4 Definire la classe NumeroReale con i metodi che restituiscono la parte intera e la parte decimale.
5 Definire la classe Approssimazione con il dato valoreIniziale e con i seguenti tre metodi:
A perDifetto
B perEccesso
C interoPiuVicino
6 Definire la classe Segmento individuata dalle coordinate sul piano cartesiano dei suoi vertici e che pos-
siede i metodi per restituire la lunghezza del segmento e le coordinate del punto medio.
7 Definire la classe ArticoloInVendita con i dati prezzoDiVendita (in euro) e descrizione. Do-
po, definire i due oggetti:
: Articolo1 (30, “maglione”)
: Articolo2 (11, “camicia”)
Definire anche una variabile static per rappresentare la percentuale di un possibile sconto sul prezzo
di vendita.
8 Definire una classe che permetta la conversione da euro in lire e viceversa (si approssimi il fattore di
conversione a 2000. Si definiscano due costruttori: il primo con il parametro long (per le lire) il secon-
do con il parametro float (per gli euro).
9 Definire una classe che, per la misura degli angoli, permetta la conversione da gradi in radianti: si defi-
niscano due costruttori: uno per la misura in gradi che ha come parametri tre interi (gradi, minuti, se-
condi) e uno per la misura in radianti che ha un unico parametro di tipo float.
q0 Definire una classe Pesata che contiene i seguenti dati: nome, altezza (in metri) e peso (in kg) e che
possegga il metodo che valuta se una persona è normolinea. Una persona è normolinea se vale la rela-
zione 23 < peso / altezza2 < 27.
qs Data una parabola y = ax2 + bx + c, definire una classe Parabola utilizzando i suoi tre coefficienti a, b,
c. Crea il suo costruttore e i metodi ascissaDelVertice(), ordinataDelVertice() e conca-
vità ().
249
Sezione 5 - Classi e oggetti
Esercizi
Unità didattica 13
qd Definire la classe Motore attraverso gli attributi cilindrata, marca, valvole. Definire l'intestazio-
ne dei metodi avvia(), accelera() e interrompi() senza implementarli. Derivare la classe Mo-
toreDiesel da Motore e ridefinire il metodo avvia().
qf Definire la classe IndirizzoGenerico con il metodo getIndirizzo(), che restituisce una stringa
vuota. Definire la classe IndirizzoEmail come classe derivata da IndirizzoGenerico, con l'at-
tributo account. Ridefinire il metodo getIndirizzo() per restituire il valore di account.
qj Definire una classe Triangolo attraverso le misure a, b, c dei suoi tre lati. Definire il suo costruttore
e il metodo per calcolare il perimetro. Derivare da Triangolo la classe TriangoloIsoscele e defi-
nire il suo costruttore. Derivare da Triangololsoscele la classe TriangoloEquilatero e defini-
re il suo costruttore.
qk Data una parabola y = ax2 + bx + c, definire una classe Parabola utilizzando i suoi tre coefficienti a,
b, c. Crea il suo costruttore e i metodi ascissaDelVertice(), ordinataDelVertice() e con-
cavità ().
250
Sezione 6
Operare con gli archivi
†
Obiettivi
generali
◊ Riconoscere lÕimportanza dellÕarchiviazione dei dati
◊ Organizzare i file di dati in modo funzionale
◊ Comprendere la necessitˆ degli archivi di dati
nelle applicazioni gestionali
◊ Conoscere le caratteristiche dei file di testo
$ Definizione di archivio
$ Definizione di record
$ Operazioni fondamentali sugli archivi
$ I/O standard e su memoria di massa
$ Tipi di archivio
$ Tipi di accesso
Unitˆ didattica 14 Archivi
I computer sono parte integrante della nostra vita da diversi anni. La loro integrazione con le nostre
attivitˆ quotidiane • talmente vasta e radicata che ormai non ci rendiamo nemmeno pi• conto della
loro presenza e di come contribuiscano, nel bene e nel male, alla nostra esistenza.
Siamo circondati dai computer praticamente in ogni momento della giornata. Infatti, pensare ai
computer solo come alle macchine che siamo abituati a vedere sui nostri tavoli al lavoro o nei labo-
ratori delle scuole e delle universitˆ, • piuttosto restrittivo: gran parte degli oggetti che ci circonda-
no possiedono al loro interno un piccolo processore. Nella sola automobile sono presenti, per esem-
pio, diversi computer: la centralina del motore, la centralina degli airbag, la centralina del condizio-
natore, la centralina degli impianti audio ecc.
Oltre ai computer come entitˆ fisiche (lÕhardware) anche i loro programmi (il software) sono par-
te integrante della nostra vita: la radiosveglia che ogni mattina interrompe i nostri sogni, infatti, •
un piccolo computer con un piccolo programma che si occupa di far scattare la suoneria allÕora pre-
stabilita.
Gran parte dei programmi esistenti non si limitano per˜ alla semplice gestione di una radiosveglia.
Pensiamo, per esempio, ai programmi che gestiscono lÕanagrafe di un Comune, oppure, pi• sempli-
cemente, a un programma che tenga aggiornato lÕelenco dei CD e dei DVD in nostro possesso. Una
funzione comune a tutti i programmi di gestione consiste nella memorizzazione, e in seguito nellÕe-
laborazione, di grandi quantitˆ di dati.
Questi dati vengono raggruppati in archivi, che generalmente sono gestiti attraverso un linguaggio
sviluppato ad hoc per interagire con essi, in grado di gestire insiemi di archivi tra loro integrati.
Gli archivi e il loro trattamento sono lÕargomento di questa Unitˆ didattica.
é stata di proposito fornita una definizione di archivio molto generale, che pu˜ dare adito a dubbi e
sollevare domande. Vediamo quindi di approfondire il concetto.
Nel mondo reale ci • ben chiaro, per esperienza diretta, in che cosa consista un archivio. In qualsiasi
ufficio ci sia capitato di passare, per esempio la segreteria della scuola, avremo sicuramente avuto mo-
do di vedere la classica cassettiera in metallo, che contiene le schede personali degli studenti.
Pertanto, un archivio pu˜ essere visto e pensato come una struttura metallica con i cassetti. Negli stes-
si uffici avremmo per˜ potuto osservare, con la stessa probabilitˆ, uno scaffale pieno di grossi racco-
glitori ad anelli con delle etichette sul dorso. In questo caso, i raccoglitori sono archivi di documenti.
Il concetto di archivio in sŽ •, pertanto, un concetto astratto, che pu˜ essere adattato a una serie di
oggetti fisici anche molto diversi lÕuno dallÕaltro. Ci˜ che accomuna tutti gli archivi • lo scopo per cui
sono creati: mantenere raccolte le informazioni.
253
Sezione 6 - Operare con gli archivi
Da un punto di vista informatico, un archivio pu˜ essere semplicemente pensato come un comunis-
simo file.
Come impareremo nel seguito della trattazione, un singolo file non costituisce una raccolta di dati
sufficiente per risolvere un problema di tipo gestionale, ma inizieremo da questo concetto per af-
frontare il tema della definizione e della strutturazione di una base di dati.
14.2 Dati
Ora che abbiamo definito un archivio in ambito informatico, possiamo esaminare gli elementi che
pu˜ contenere.
Abbiamo infatti visto come sia possibile, in generale, pensare a un archivio come a un contenitore di
dati. Il concetto di dato • un concetto astratto esattamente come quello di archivio. In pratica, in che
cosa consistono i dati che ÒsalviamoÓ in un archivio?
Si pu˜ definire dato una qualsiasi informazione che si vuole registrare o memorizzare
allÕinterno di un computer.
Ancora una volta, la definizione lascia il campo aperto a molte considerazioni, essendo il concetto
molto generale. Vediamo quindi qualche esempio.
In questo caso, il dato da salvare • la data di sistema e lÕarchivio consiste in un singolo file di testo,
dove ci si limita a scrivere la data.
...........................................................................................................................................
LÕutilitˆ di un tale programma pu˜ sembrare piuttosto limitata. Tuttavia non dobbiamo dimenticare che
ogni programma, per quanto piccolo e ÒstupidoÓ, pu˜ essere parte di un sistema che diventa molto com-
plesso, se visto nel suo insieme. Quando si accede a una macchina Unix, per esempio, il sistema indica
sul terminale quando lÕutente si • interfacciato con il sistema lÕultima volta.
In questo caso il dato da salvare non • pi• semplice, ma consiste in diversi ÒpezziÓ di informazione.
Anche in questo caso, tuttavia, possiamo limitarci a salvare le varie parti come se si trattasse di co-
munissimo testo, separandole tra loro con un carattere di separazione (generalmente la virgola).
LÕarchivio, ancora una volta, consiste di un singolo file di testo.
...........................................................................................................................................
254
Unità didattica 14 - Archivi
Esempio Studenti................................................................................................................
Scrivere un programma per gestire l’elenco degli studenti di una scuola. Il programma de-
ve essere in grado di associare un elenco di studenti con la classe e la sezione a cui essi
appartengono.
A prima vista, questo caso sembra più semplice dei precedenti. In realtà è leggermente più comples-
so, poiché a una singola informazione (la classe e la sezione, che consideriamo un unico dato), deve
venire associato un elenco più o meno numeroso di nomi (in realtà, di nomi e cognomi che, per sem-
plicità, possiamo pensare come un unico dato).
La soluzione più semplice consiste nel salvare più file, uno per ogni classe, nominandoli con il nome
della classe e della sezione (per esempio, 3A). All’interno di ogni singolo file, i nomi degli alunni sa-
ranno scritti in formato testuale, separando i nomi dai cognomi con un semplice spazio (o con un al-
tro carattere separatore).
In questo caso, quindi, l’archivio è in realtà suddiviso in molti file (uno per ogni classe dell’istitu-
to), e i dati in esso contenuti sono banale testo che, eventualmente, comprende un carattere sepa-
ratore.
...........................................................................................................................................
Ora che sappiamo in che cosa consistono i dati, possiamo approfondire ulteriormente il discorso.
Nell’esempio “Diario” del paragrafo precedente, il singolo record è costituito dall’insieme di infor-
mazioni che sono logicamente raggruppate tra loro, ovvero:
: data;
: ora;
: luogo;
: annotazione.
Dal punto di vista grafico, un record può essere pensato come la riga di una tabella:
Data Ora Luogo Annotazione
Chiaramente, anche le singole unità di informazione che formano un record hanno un nome.
Le parti logiche in cui viene suddiviso un singolo record prendono il nome di campi.
Normalmente, il nome del campo deriva dal tipo di informazione che è associata al
campo stesso.
Risulta piuttosto comodo poter fare riferimento ai singoli campi assegnando loro un nome: nell’e-
sempio “Diario”, i nomi dei campi potrebbero essere proprio “Data”, “Ora”, “Luogo” e “Annota-
zione”.
255
Sezione 6 - Operare con gli archivi
Poiché ora siamo in grado di gestire singoli record, possiamo riformulare la soluzione nel modo de-
scritto di seguito. Per ogni iscritto all’istituto conosciamo il nome, il cognome, la classe e la sezione.
Il record in questo caso è (schematicamente) il seguente:
Sezione Classe Nome Cognome
A questo punto, l’archivio potrà consistere in un unico file, che contiene i record sopra descritti, con
i singoli campi separati da un carattere separatore, come per esempio il punto e virgola.
...........................................................................................................................................
Ora che abbiamo imparato un po’ di teoria, possiamo passare alla pratica per gestire queste strutture
di dati.
: apertura dell’archivio;
: lettura dei record;
: scrittura dei record;
: modifica dei record (compresa la cancellazione);
: chiusura dell’archivio.
Queste operazioni possono sembrare piuttosto banali, ma ancora una volta una riflessione più atten-
ta potrebbe portarci a riclassificare il problema.
Consideriamo, per esempio, la semplice apertura dell’archivio. L’operazione di apertura dell’archi-
vio può cambiare a seconda del tipo e del numero di file che si stanno aprendo. In generale, l’opera-
zione è abbastanza semplice e consiste in una chiamata alla funzione di sistema di apertura del file.
Per completezza, scriviamo una funzione che apre un file il cui nome viene passato come parametro.
256
Unitˆ didattica 14 - Archivi
. . . .
. . . .
31 dati.close();
. . . .
. . . .
Al momento dellÕapertura di un file devono essere specificati due nomi: il nome logico a cui farˆ riferi-
mento il programma e il nome fisico, che • quello utilizzato dal file system del sistema operativo.
NellÕesempio precedente il nome logico del file • dati, mentre il suo nome fisico • dati.txt. Ci˜
significa che le operazioni sul file dati specificate nel programma vengono fisicamente eseguite sul fi-
le dati.txt.
257
Sezione 6 - Operare con gli archivi
Tastiera
Memoria centrale
Programma
Utilizzando i simboli descritti sopra, lo standard input e lo standard output sono descritti grafica-
mente nello schema sottostante.
Output
Input
258
Unitˆ didattica 14 - Archivi
Output / scrivi
Input / leggi
Normalmente, nella descrizione delle applicazioni viene indicato anche il programma, mentre il ter-
minale viene rappresentato con il simbolo sottostante.
Se, per esempio, si vuole schematizzare un programma che acquisisce dati da tastiera e li registra su
un disco magnetico, ci si dovrˆ affidare al disegno che segue.
Registra.exe
Dati.dat
Dove ÒRegistra.exeÓ • il nome del programma e ÒDati.datÓ • il nome del file creato dal programma.
In ambiente Windows, i file di configurazione hanno solitamente estensione .ini e si trovano nella di-
rectory del sistema. Possono essere divisi per sezioni, ma contengono tutte le informazioni necessarie
per configurare un programma. Anche se ultimamente si preferisce usare il registro di configurazione del
sistema al posto dei file di inizializzazione, in alcuni programmi lÕelenco dei file utilizzati pi• di recente si
trova nel file di configurazione.
Proseguendo nella lettura degli esempi proposti in questa Unitˆ didattica, possiamo notare come i
dati vengano spesso strutturati sotto forma di record. In questo caso, come viene organizzato lÕar-
chivio? Una metodologia piuttosto semplice per risolvere i problemi consiste nel rifarsi a un caso pre-
cedente di cui si conosce giˆ la soluzione. In questo caso, possiamo rifarci nuovamente al file di te-
sto. Si tratta solamente di decidere come suddividere i dati che fanno parte dei record. Tipicamente,
si sceglie un carattere separatore e lo si utilizza per dividere fisicamente i dati.
259
Sezione 6 - Operare con gli archivi
Il formato di file CSV (Comma Separated Values, cioè valori separati da virgola), uni-
versalmente adottato, prevede proprio che i campi del record vengano separati dal
carattere “,” (virgola).
Quando si sviluppano programmi ad hoc, tuttavia, non sempre si adottano gli standard di sviluppo
internazionali, ma si impiega il carattere meno utilizzato dall’applicazione (per esempio “#”).
La metodologia appena illustrata non è certamente l’unica quando si vuole gestire un file di record,
ma essa offre una certa facilità di implementazione e di gestione. Per questo motivo è largamente uti-
lizzata nelle applicazioni create ad hoc, mentre per le basi di dati commerciali (Oracle, SQLServer,
MySQL) si utilizzano altre tecniche più complesse. L’illustrazione di queste ultime esula dagli scopi
di questo libro, e pertanto si rimanda il lettore alla letteratura specializzata.
Un ultimo metodo per salvare i dati in un archivio consiste nel salvataggio a byte. In
questo caso, i singoli record vengono scritti sull’archivio come un flusso continuo. Poi-
ché la quantità di byte occupata dal singolo record e dai campi che lo compongo-
no è nota a priori, l’estrazione dei dati avviene tenendo conto della lunghezza fissa
dei record.
In altre parole, supponiamo che un database contenga un milione di record, e il nostro obiettivo sia
leggere l’ultimo record contenuto nell’archivio. Non c’è alcun modo per posizionarsi sull’ultimo re-
cord se non quello di leggere preventivamente tutti i 999.999 record che lo precedono. Chiaramen-
te, un file ad accesso sequenziale non è molto utile nel caso in cui si vogliano salvare molti dati, a cau-
sa della pesantezza nella sua elaborazione (è sempre necessario partire dal primo record e scorrerli
tutti). Solitamente, gli archivi di tipo sequenziale sono file di testo dove vengono salvati pochi dati
utili per l’esecuzione di un programma. In questo caso, poiché il programma, per funzionare, deve
leggere tutte le impostazioni di configurazione, diventa agevole per il programmatore utilizzare un
file ad accesso sequenziale, che è molto semplice da gestire.
Supponiamo sempre di avere un archivio con un milione di record al suo interno e che il nostro
obiettivo sia leggere l’ultimo record. Poiché questa volta l’archivio è ad accesso diretto, possiamo po-
sizionarci subito sull’ultimo record e leggerlo senza prima scorrere tutti gli altri record. Questo tipo
di accesso è molto utile nel caso in cui si stia trattando un archivio con dati strutturati organizzati a
record, in quanto, a parte l’apertura del file, non c’è alcuna operazione preventiva da compiere, e
quindi il tempo di accesso a un singolo dato si abbassa.
260
Unità didattica 14 - Archivi
Esercizi
Unità didattica 14
1 Le parti logiche in cui viene suddiviso un singolo record prendono il nome di ......................................
3 Nel primo schema della figura che segue la freccia indicare un’operazione di .....................................
nel secondo un’operazione di .........................................................................................................
5 Completare la definizione:
Un ........................... è l’unità logica di memorizzazione dei dati all’interno di un supporto informatico.
Per supporto si intende un disco fisso, un floppy, un CD-ROM, un DVD oppure ogni .............................
6 Completare la definizione:
Si può definire ...................................... qualsiasi informazione che si vuole registrare o memorizzare
all’interno di un computer.
7 Completare la definizione:
Si dice operazione di .............................. l’operazione che permette di trasferire un dato o un insieme
di dati da una unità periferica nella memoria RAM.
Si dice operazione di ....................................... il trasferimento di un dato o di un insieme di dati dalla
memoria RAM alla periferica.
8 Che cosa si intende per unità standard di input e per unità standard di output?
Registra.exe
Dati.dat
q0 Completare la definizione:
Si accede a un file in modo ............................................ se tutte le operazioni di lettura e di scrittura
possono avvenire solo scorrendo il file dall’inizio fino al punto in cui vogliamo leggere o scrivere i dati.
qa Completare la definizione:
Si accede a un file in modo ............................................ se tutte le operazioni di lettura e di scrittura
possono avvenire in modo puntuale sul singolo record, indipendentemente dalla sua posizione all’inter-
no dell’archivio.
261
Unitˆ didattica
15
File di testo
Esempio CreaDati..............................................................................................................
Registrare in un file sequenziale una serie di numeri inseriti da tastiera.
Poiché il problema non specifica quanti sono i numeri da inserire, per ogni inserimento si deve ripe-
tere anche la domanda se l’elenco è finito. Si deve, quindi, realizzare una ripetizione che contenga le
seguenti istruzioni:
Queste quattro operazioni devono essere eseguite almeno una volta e devono essere ripetute per
ogni numero “mentre” l’elenco non è finito, ovvero quando la risposta alla domanda “elenco fini-
to?” è uguale a “n”.
Pseudocodifica
INIZIO
Apri il file in scrittura
SE operazione NON ha avuto successo
ALLORA
termina programma
FINE SE
263
Sezione 6 - Operare con gli archivi
RIPETI
chiedi e leggi un numero da tastiera
scrivi un numero nellÕarchivio di dati
chiedi Òelenco finito ? (s/n)Ó
leggi la risposta
MENTRE risposta = ÒnÓ
Chiudi file
FINE
CreaDati.exe
dati.txt
Codice
CreaDati.cpp
1 #include <iostream>
2 #include <fstream>
3 #include <string>
4 using namespace std;
5
6 //INIZIO
7 int main ()
8 {
9 double numero;
10 string risp;
11
12 //apri file dati in scrittura
13 ofstream dati("dati.txt");
14
15 if(!dati) //se l'operazione non ha avuto successo
16 {
17 //termina programma
18 cout << "ERRORE apertura file";
19 //fine programma
20 cout << "\n\nFine ";
21 system ("pause");
22 return 1 ;
23 }
24
25 do //RIPETI
26 {
27 //chiedi e leggi un dato da tastiera
28 cout << "Inserisci un numero: ";
29 cin >> numero;
30
31 //scrivi un dato nellÕarchivio di dati
32 dati << numero << "\n" ;
33
34 //chiedi se elenco finito
264
Unitˆ didattica 15 - File di testo
Prova di esecuzione
Per verificare i buon esito della prova di esecuzione si pu˜ leggere il contenuto del file dati.txt,
utilizzando il programma Blocco note di Windows.
265
Sezione 6 - Operare con gli archivi
L’operazione di apertura contiene il nome fisico del file dati.txt, ma non è indicato il percorso di tale
file. La conseguenza è che il file di testo viene creato all’interno della cartella in cui si trova il program-
ma; se si vuole creare il file in un’altra cartella si deve indicare, oltre al nome, anche il percorso: per
esempio “C:\archivi\dati.txt” .
...........................................................................................................................................
Tali operazioni devono essere ripetute fintantoché non si raggiunge la fine delle righe che compon-
gono il file.
Per segnalare che è stata raggiunta la fine del file si deve utilizzare il metodo eof()
(“eof” sta per End of File). Tale metodo restituisce un valore booleano: true se è stato
fatto un tentativo di lettura oltre la fine del file, false altrimenti.
dati.txt LeggiDati.exe
Pseudocodifica
INIZIO
apri il file dati.txt in lettura
SE il file non esiste
termina il programma
leggi un numero dal file
FINE SE
MENTRE il file non è finito
scrivi un numero a video
leggi un numero dal file
266
Unitˆ didattica 15 - File di testo
RIPETI
chiudi il file
FINE
Nella pseudocodifica si nota che la prima operazione di lettura • stata posta al di fuori della struttura
di ripetizione MENTRE-RIPETI (si dice che viene effettuata una Òlettura fuori giroÓ), mentre le al-
tre istruzioni di lettura (compresa lÕultima) si trovano allÕinterno della struttura di ripetizione. La
scelta del costrutto MENTRE-RIPETI porta ad eseguire unÕoperazione di lettura in pi•, quello del
carattere di fine file che fa restituire true al metodo eof(); inoltre, dopo tale lettura non si deve
eseguire alcuna elaborazione. La lettura iniziale (fuori giro) • necessaria per fare iniziare il ciclo di ri-
petizione: infatti, se il file non • vuoto risulta dati.eof() = false.
Codice
LeggiDati.cpp
1 #include <iostream>
2 #include <fstream>
3 #include <string>
4 using namespace std;
5
6 //INIZIO
7 int main ()
8 {
9 double numero;
10 //apri il file in lettura
11 ifstream dati("dati.txt");
12
13 if(!dati) //SE il file non esiste
14 {
15 //termina programma
16 cout << "ERRORE apertura file";
17 //fine programma
18 cout << "\n\nFine ";
19 system ("pause");
20 return 1 ;
21 }
22
23 //leggi il primo numero del file
24 dati >> numero;
25
26 while (!dati.eof()) //MENTRE il file non • finito
27 {
28 //scrivi il numero letto a video
29 cout << numero << "\n";
30 //leggi un numero
31 dati >> numero;
32 } //RIPETI
33
34 //chiudi il file
35 dati.close();
36
37 //fine programma
38 cout << "\nFine ";
39 system ("pause");
40 return 0;
41 }
267
Sezione 6 - Operare con gli archivi
Prova di esecuzione
15.3 Accodamento
Oltre alle istruzioni di apertura dei file viste nei due esempi precedenti (in output o in input), esiste
una terza modalità, più accurata delle precedenti, che permette di specificare in maniera più detta-
gliata il criterio di apertura di un file.
La modalità con cui viene aperto un file può essere specificata mediante i flag di mo-
dalità di apertura, i cui valori possono essere scelti tra quelli dell’enumerazione
openmode, che è un membro pubblico della classe base degli stream ios_base.
Eccoli:
: ios_base::app: il file viene aperto posizionandosi in fondo in modo permanente,
cioè i nuovi dati vengono inseriti sempre in fondo al file (modalità “Append”);
: ios_base::ate: il file viene aperto posizionandosi inizialmente in fondo (modalità
“At The End”);
: ios_base::binary: il file viene aperto in modo binario, cioè trattandone il
contenuto come puri dati;
: ios_base::in: il file viene aperto in lettura (modalità “Input”);
: ios_base::out: il file viene aperto in scrittura (modalità “Output”);
: ios_base::trunc: il file viene aperto cancellandone l’eventuale contenuto
esistente, creandolo se non esiste.
Si ricorda che, per default, un file viene aperto posizionandosi sul primo carattere e
trattandone il contenuto come testo, producendo una segnalazione di errore nel ca-
so si stia tentando di aprire un file non esistente.
268
Unitˆ didattica 15 - File di testo
Particolare interesse riveste la modalitˆ ios_open::app, che permette di accodare (append, in in-
glese) nuovi dati a un file giˆ esistente.
Esempio Accoda................................................................................................................
Aggiungere altri numeri al file dati.txt, senza cancellare i dati preesistenti.
Codice
Accoda.cpp
1 #include <iostream>
2 #include <fstream>
3 #include <string>
4 using namespace std;
5
6 //INIZIO
7 int main ()
8 {
9 double numero;
10 string risp;
11
12 //apri file dati in append
13 fstream dati;
14 dati.open(Òdati.txtÓ, ios::out|ios::app);
15
16 if(!dati) //se l'operazione non ha avuto successo
17 {
18 //termina programma
19 cout << "ERRORE apertura file";
20 //fine programma
21 cout << "\n\nFine ";
22 system ("pause");
23 return 1 ;
24 }
25
26 do //RIPETI
27 {
28 //chiedi e leggi dato da tastiera
29 cout << "Inserisci un numero: ";
30 cin >> numero;
31
32 //scrivi dato su archivio dati
33 dati << numero << "\n" ;
34
35 //chiedi elenco finito
36 cout << "Elenco finito? (s/n) ";
37 cin >> risp;
38 }
39
40 while (risp == "n"); //MENTRE elenco NON finito
41
42 //chiudi file
43 dati.close();
44
45 //fine programma
269
Sezione 6 - Operare con gli archivi
Prova di esecuzione
Anche in questo caso il buon esito del programma pu˜ essere facilmente verificato mediante la lettu-
ra dei dati contenuti nel file dati.txt, aprendolo con Blocco note.
13 ifstream dati("dati.txt");
13 fstream dati;
14 dati.open(ÒDati.txtÓ, ios::out|ios::app);
Come si vede, la riga 13 definisce lÕoggetto dati come di classe fstream, la riga 14 specifica che il
file deve essere aperto in scrittura (modalitˆ ios::out) e con lÕobbligo di inserire i nuovi dati in
fondo al file (modalitˆ ios::app). Per lÕapertura di un file possono essere specificate diverse mo-
dalitˆ, combinate tra di loro dallÕoperatore di OR bit a bit (indicato nel codice con il simbolo Ò|Ó):
ogni flag differisce infatti dagli altri per un solo bit.
...........................................................................................................................................
270
Unità didattica 15 - File di testo
Esercizi
Unità didattica 15
7 Disegnare lo schema del flusso dei dati del programma dell’esercizio precedente.
8 Creare un file di testo per archiviare una lista di nomi con relativo numero telefonico; il nome e il nume-
ro telefonico devono essere separati da un punto e virgola.
9 Aggiungere un nuovo nome e relativo numero telefonico al file creato nell’esercizio precedente.
q0 Trascrivere in un file di testo i dati di una matrice di tre righe e quattro colonne: ogni riga della matrice
occupa una riga del file, e i numeri sono separati da una virgola.
qf Quale struttura di controllo contiene l’algoritmo usato per leggere tutte le righe di un file di testo?
A Selezione
B Selezione multipla
C Ripetizione precondizionale
D Ripetizione postcondizionale
E Ripetizione enumerativa
qg L’istruzione C++ per leggere una riga da un file di testo ha la seguente sintassi: ................................
271
Sezione 6 - Operare con gli archivi
Esercizi
Unità didattica 15
qj Trascrivere in un file di testo le prime due terzine della Divina Commedia. Successivamente, con un al-
tro programma, visualizzare le terzine.
qk Disegnare lo schema del flusso dei dati dei programmi dell’esercizio precedente.
ql Creare un file di testo per archiviare una lista di nomi con relativo numero telefonico; il nome e il nume-
ro telefonico devono essere separati da un punto e virgola. Successivamente, con un altro programma,
visualizzare il contenuto del file senza mostrare il punto e virgola inserito come separatore.
w0 Creare un file di testo per archiviare una lista di nomi con relativo numero telefonico; il nome e il nume-
ro telefonico devono essere separati da un punto e virgola. Successivamente, con un altro programma,
mostrare il numero telefonico di un nome inserito da tastiera.
wa Creare un file di testo dove sono archiviati i cognomi e i nomi degli studenti di una classe. Successiva-
mente, mostrare cognome e nome degli studenti (possono essere più di uno) che hanno cognome ugua-
le a un cognome inserito da tastiera.
272
Sezione 7
Le eccezioni
†
Obiettivi ◊ Costruire programmi robusti
◊ Prevenire gli errori di risposta dellÕutente
◊ Tenere sotto controllo le anomalie del sistema
operativo
ErroreDivisione.cpp
1 #include <iostream>
2 using namespace std;
3 //INIZIO
4 int main ()
5 {
6 int dividendo;
7 int divisore;
8
9 //chiedi e leggi dividendo
10 cout << "Inserisci il valore del dividendo ";
11 cin >> dividendo;
12
13 //chiedi e leggi divisore
14 cout << "Inserisci il valore del divisore ";
15 cin >> divisore;
16
17 //dichiara la variabile per il risultato
18 int divisione;
19
20 //esegui lÕoperazione
21 divisione=dividendo/divisore;
22
23 //scrivi il risultato
24 cout << "\n Divisione = " << divisione << endl;
25
26 //fine programma
27 cout << "\nFine ";
28 system ("pause");
29 return 0;
30 }
Il compilatore non trova nulla da segnalare, quindi il programma risulta corretto e, se i dati inseriti
sono validi, si ottiene quanto riportato nella prova di esecuzione che segue.
275
Sezione 7 - Le eccezioni
Prova di esecuzione
Nel programma, per˜, non • presente alcun controllo sulla validitˆ dei dati e, se lÕutente inserisce 0
come valore per il divisore, succede quanto riportato nella figura che segue.
Come si pu˜ vedere, si • verificata unÕanomalia che produce un arresto del programma e non per-
mette di procedere nellÕesecuzione dellÕoperazione.
...........................................................................................................................................
UnÕanomalia del codice consiste in una situazione ingestibile e rilevabile solo duran-
te lÕesecuzione del programma.
Le anomalie sono quasi sempre dovute a errori di progettazione o di scrittura del codice: solo la pro-
gettazione e la stesura attenta possono evitarci di incorrere in anomalie del codice.
Un modo per difendersi dalle anomalie consiste nella gestione degli errori.
Per codice di gestione degli errori si intende quella parte di codice che si occupa di
gestire lÕeventualitˆ che si verifichino errori a run-time (cio• in fase di esecuzione).
Nel caso visto sopra della divisione per zero, la gestione degli errori pu˜ essere realizzata modifican-
do il codice ErroreDivisione.cpp come indicato di seguito.
21 if (divisore !=0)
22 {
23 //esegui lÕoperazione
276
Unitˆ didattica 16 - Gestione delle eccezioni
24 divisione=dividendo/divisore;
25
26 //scrivi il risultato
27 cout << "\n Divisione = " << divisione << endl;
28 }
29 else
31 {
32 cout << "\n Divisione impossibile " << endl;
33 }
34 //fine programma
35 . . .
36 . . .
37 }
Il fatto che il codice controlli il valore della variabile divisore fa parte di una buona procedura di
gestione degli errori, perchŽ previene le anomalie di esecuzione.
16.2 Eccezioni
Quanto abbiamo detto fino a questo momento sulla gestione degli errori vale in qualsiasi linguaggio
di programmazione. Il fatto che le procedure per la gestione degli errori non siano standardizzate fa
s“ che ognuno di noi adotti un proprio stile di programmazione, anche se in generale • opportuno
seguire uno schema o unÕindicazione di massima per la stesura del codice; negli anni, per favorire la
creazione di uno standard, • stato introdotto il concetto di ÒeccezioneÓ.
La gestione delle eccezioni consiste in una sintassi standard che definisce le direttive di massima per
il trattamento delle anomalie di esecuzione, lasciando tuttavia il programmatore libero di realizzare
il codice per la loro gestione nel modo che ritiene pi• opportuno.
La gestione delle eccezioni non costituisce quindi, di per sŽ, la gestione degli errori, bens“ fornisce
un metodo per realizzare questÕultima.
Un blocco try consiste nella parola chiave try seguita da un blocco di codice
racchiuso da parentesi graffe, la cui esecuzione viene controllata dal gestore delle
eccezioni.
Vediamo come riscrivere il codice dellÕesempio precedente, alla luce di quanto ora detto. Per farlo ab-
biamo bisogno, per˜, di affrontare il concetto di generazione delle eccezioni.
La generazione di unÕeccezione avviene attraverso il comando throw, che provoca lÕuscita dal blocco
di programmazione in cui tale parola chiave si trova e trasferisce il controllo al gestore dellÕeccezione.
277
Sezione 7 - Le eccezioni
Il comando throw riceve in input un solo argomento, che pu˜ essere di qualsiasi tipo. In base al ti-
po dellÕeccezione generata il gestore sarˆ diverso. Vediamo un codice dÕesempio chiarificatore.
Codice
Eccezione.cpp
1 #include <iostream>
2 using namespace std;
3 //INIZIO
4 int main ()
5 {
6 int dividendo;
7 int divisore;
8
9 //chiedi e leggi il dividendo
10 cout << "Inserisci il valore del dividendo ";
11 cin >> dividendo;
12
13 //chiedi e leggi il divisore
10 cout << "Inserisci il valore del divisore ";
15 cin >> divisore;
16
17 //dichiara la variabile per il risultato
18 int divisione;
19
20 try
21 {
22 //controlla divisore
23 if (divisore == 0)
24 {
25 throw 100;
26 }
27
28 //esegui la divisione
29 divisione = dividendo/divisore;
30
31 //scrivi il risultato
32 cout << "\n Divisione = " << divisione << endl;
33 }
34
35 catch (int codice)
36 {
37 cout << "Codice errore = "<< codice << endl;
38 cout << "programma interrotto ";
39 }
40
41 //fine programma
42 cout << "\n\nFine ";
43 system ("pause");
44 return 0;
45 }
278
Unitˆ didattica 16 - Gestione delle eccezioni
Prove di esecuzione
AllÕinterno di un blocco try possono essere analizzate diverse situazioni di errore e, quindi, posso-
no essere presenti pi• istruzioni throw. Per ogni valore associato a throw deve essere poi presente
un blocco di istruzioni corrispondente, in grado di gestire lÕeccezione generata.
Il blocco di gestione delle eccezioni • caratterizzato da una o pi• parole chiave catch, ciascuna se-
guita dal proprio blocco di programmazione; ogni parola chiave catch deve, perci˜, essere seguita
da una coppia di parentesi graffe.
Esempio ErroriDiversi..........................................................................................................
é dato un vettore di 10 interi, giˆ presente in memoria. Scrivere un programma che richie-
da allÕutente lÕindice di un elemento del vettore e restituisca il logaritmo naturale dellÕele-
mento corrispondente del vettore individuato.
279
Sezione 7 - Le eccezioni
Le prime due anomalie sono legate a quanto specifica lÕutente, mentre la terza dipende dal contenu-
to del vettore: in tutti e tre i casi la situazione genera errori di esecuzione, quindi deve essere pro-
dotto un programma che controlli ci˜ di cui non si conoscono gli effetti.
Codice
ErroriDiversi.cpp
1 #include <iostream>
2 #include <cmath>
3 using namespace std;
4
5 //INIZIO
6 int main ()
7 {
8
9 int indice;
10
11 //definisce dimensione
12 const int Dim = 10;
13 //definizione e inizializzazione vettore
14 double vett [Dim]={1,-8,-3,7,-6,6,13,-8,9,10};
15
16 try
17 {
18 //Chiedi e leggi indice
19 cout << "indica il numero di indice dellÕelemento richiesto ";
20 cin >> indice;
21 //controlla indice
22 if (indice < 0) throw 1; // anomalia 1 codice errore di tipo int
23 if (indice > Dim) throw 2; // anomalia 2 codice errore di tipo int
24
25 //scrivi lÕelemento individuato dallÕindice
26 cout << "\nElemento di posto " << indice << " = " << vett[indice];
27
28 //controlla se elemento • positivo
29 if (vett[indice] <= 0) throw 0.5; // anomalia 3 errore di tipo double
30
31 //se tutto ok scrive logaritmo
32 cout << "\n\nLogaritmo = " << log(vett[indice]);
33 }
34
35 catch (int codice) // gestisce codici errore di tipo int
36 {
37 switch (codice)
38 {
39 case 1: cout<< "\nErrore = indice negativo";break;
40 case 2: cout<< "\nErrore = indice fuori limite massimo";
41 }
42 }
43
44 catch (double err) // gestisce codici errore di tipo double
45 {
46 cout << "\n\nLogaritmo impossibile ";
47 }
48
280
Unitˆ didattica 16 - Gestione delle eccezioni
49 //fine programma
50 cout << "\n\nFine ";
51 system ("pause");
52 return 0;
53 }
Prove di esecuzione
In questa prima prova il dato inserito • corretto e lÕelemento del vettore • positivo, pertanto non si
rilevano anomalie e lÕesecuzione del programma va a buon fine.
281
Sezione 7 - Le eccezioni
Alla riga 22 viene individuata lÕanomalia riguardante lÕindice negativo e generato il codice del corri-
spondente gestore di eccezione. Analogamente alla riga 23: viene individuata lÕanomalia riguardan-
te lÕindice superiore a 10 e generato il corrispondente codice di gestione dellÕeccezione.
Da ultimo, prima di calcolare il logaritmo, viene verificato che lÕargomento del logaritmo sia un nu-
mero positivo; in caso contrario, alla riga 29 viene generato il nuovo codice di eccezione, pari a 0.5.
Si noti che i primi due codici delle anomalie sono di tipo intero e il terzo • di tipo double.
Alla riga 35 il primo gestore di eccezioni, introdotto dalla parola chiave catch, ha come parametro
una variabile di tipo intero, quindi raccoglie le anomalie individuate dai codici 1 e 2.
AllÕinterno del blocco di istruzioni compreso tra la riga 36 e la 42 i due valori interi del codice ven-
gono considerati e trattati singolarmente.
LÕaltro gestore di eccezioni si trova alla riga 44, ha come parametro una variabile di tipo double e
gestisce lÕanomalia di esecuzione segnalata con il codice 0.5.
...........................................................................................................................................
282
Unità didattica 16 - Gestione delle eccezioni
Esercizi
Unità didattica 16
1 Completare:
“Un’anomalia del codice consiste in una .................................................................. e rilevabile solo
durante l’esecuzione del programma”.
2 Completare:
“Le anomalie sono quasi sempre dovute a ....................................................... Solo la progettazione
e la stesura attenta possono evitarci di incorrere in anomalie del codice. Un modo per difendersi dalle
anomalie consiste nella gestione degli errori”.
3 Completare:
“La gestione degli errori è quella parte di codice che gestisce l’eventualità di un errore a ....................
..................................................................................................................................................”.
4 Completare:
“In C++ si attiva un’eccezione con la parola chiave ............................................................ si cattura
l’eccezione con la parola chiave ...................................................................................................”.
5 Completare:
“La parola chiave try è seguita da ..............................................................................................”.
6 Scrivere gli elementi della successione S = 2i con i = 1, 2, ....... n. Catturare l’eccezione i > 30. Se S è
definito intero, quali valori assume S per i > 30?
7 Dati in input due numeri x e y, calcolare pow(x,y). x e y siano definiti double. Catturare le eccezio-
ni che si generano quando non vengono rispettate le seguenti condizioni:
: se x > 0, y può essere un valore qualsiasi;
: se x = 0, y deve essere > 0;
: se x < 0, y deve essere intero.
8 Calcolare log(x), con x dato in input. Controllare che x sia > 0 e gestire l’eccezione.
9 Calcolare sqrt(x), con x dato in input. Controllare che x sia > = 0 e gestire l’eccezione.
q0 Dati in input due interi a e b, calcolare a*b. Gestire la situazione anomala che si genera quando a*b ri-
sulta maggiore del massimo consentito per gli interi.
qa Dati in input due reali a e b, calcolare a*b. Gestire la situazione anomala che si genera quando a*b ri-
sulta esterno agli intervalli previsti per i dati di tipo double.
qs Dati in input due reali a e b, calcolare il rapporto a/b. Gestire in modo distinto le due situazioni anomale
a!=0, b=0 e a=0, b=0.
qd È dato un vettore di 10 interi già definito all’interno del programma. Viene richiesto all’utente l’indice del-
l’elemento desiderato e il programma restituisce l’intero corrispondente presente nel vettore. Catturare
l’eccezione “out of range”.
283
Appendice
Riepilogo degli operatori
A
z = x - y ;
significa
z = (x - y) ;
perchŽ Ð ha precedenza rispetto a =.
Gli operatori di pref“sso unario e quelli di assegnazione associano da destra a sinistra.
Tutti gli altri operatori associano da sinistra a destra. Per esempio:
x - y - z
significa
(x - y) - z
perchŽ lÕoperatore Ð associa da sinistra a destra, mentre
x = y = z
significa
x = (y = z)
perchŽ lÕoperatore = associa da destra a sinistra.
285
Appendice
Sequenze di caratteri escape
B
Queste sequenze di escape possono essere utilizzate nelle stringhe (per esempio Ò\nÓ) e nei tipi char
(per esempio Ô\ÔÔ ).
286
Indice analitico
A compilazione 36
errori di 37
funzione 144
membro 216
accesso complemento a due 25-28
ai file 260
ai membri di una classe 202
concatenazione di stringhe 197 G
confronto lessicografico gestione delle eccezioni 277
algoritmo 92, 94-96 di stringhe 201
allocare 42 gestore dellÕeccezione 279
conversione
analisi del problema 91
anomalia 276
dalla base 2 alla base 10 12
dalla base 10 alla base 2 12 I
append 269 costanti 49, 96 immissione (input) 95
archivio 253-254 letterali 99 incapsulazione 211
argomenti 96 costruttore 204, 224 indice di un vettore 171
CSV (Comma Separated inizializzazione
B Values) 260 di una variabile 98
INIZIO (di un algoritmo) 108
base di un sistema
di numerazione 9 D input 95, 99, 257
bit 11 dati 96, 254 -255 istanza di una classe 218
di input 95 istruzioni 93, 96
blocco try 277
di output 95 catch 277
byte 22
membro 216 cin 55
booleani 42, 48
definizione circolare 161 class 218
C dichiarazione di variabili 40
dimensione di un vettore 171
di assegnamento 97
di lettura (di input) 99
campi 255 distruttore 225 di scrittura (di output) 99
caratteri di escape 62 di ripetizione,
caratteristica 29
case sensitive 42 E o iterative 128-138
for 136
casting 48 eccezioni 277-282 if 113-114
esplicito 46 cattura delle 277 while 128-130
cicli 128 editor di testi 35
classe 215 emissione (output) 95
entry point 145
L
base 240 librerie 36
derivata 240 eof() 266
ereditarietˆ 213-214 linker 36
di problemi 94 lunghezza di una stringa 195-197
metodi 216
codice F M
ASCII 22-24 file 253-254
del calcolatore 22 dÕintestazione 36 mantissa 29
di gestione degli errori 276 eseguibile 36 matrici 180-187
macchina 22 sorgente 36 membri di una classe 216
numerico decimale 21 FINE (termine MENTRE ... FINE
UNICODE 24, 45 di un algoritmo) 108 MENTRE 108
codifica 21 flag di modalitˆ di apertura metodo della virgola mobile 29
N
comando 96 di un file 268
commenti 38 forma normalizzata
compilatore 36 di un numero 29 notazione puntata 202
287
Indice analitico
O programma sorgente 36
pseudocodifica 97, 101, 107-108
T
operatore throw 277
di negazione (NOT) 84
di risoluzione del campo
R tipo di dato
bool 48
di azione (::) 230 record 255 char 44-45
logico di valutazione redirezione 44 decimale 42
completa 82 repertorio del calcolatore 22 double 46
operatori ricorsione 161 enumerativo 169
aritmetici composti 73-78 RIPETI ... FINCHƒ 108 float 47
binari 67 ripetizione in virgola mobile 42, 46-47
logici 80-85 postcondizionale 106 int 43
post-fissi 76-77 precondizionale 104 intero 42-44
pre-fissi 76-77 risolvere un problema 91 long 44
relazionali (di confronto) ordinale 169
78-80, 200 S short 44
unari 75-76 salvataggio a byte 260 vettore 171
operazione di lettura SCEGLI ... CASO ... FINE try 277
(di input) 257 SCEGLI 108
operazione di scrittura
(di output) 257
SE ... ALLORA ... U
ALTRIMENTI ... FINE UNICODE 44-45
output 95, 99, 257 SE 108
overloading 234 unitˆ
selettore (variabile) 121
di standard input 258
set di caratteri
P del calcolatore 22
di standard output 258
parola 25
parser 36
setw 59
sistema di numerazione V
passaggio dei parametri binario 11 variabili 40
per valore 153-157 sopraclasse 214 ambiti 148
PER ... DA ... A ... PASSO ... sottoclasse 214 di controllo (selettori) 121
FINE PER 108 stringa 39, 193-206 di lavoro 95
polimorfismo 212, 237 struct 202 globali 149,151
private 216 strutture di ripetizione 104-107 locali 151
procedura 145 substr 198 vettori 172-178
288