1. Introduzione al C++
Tipi integrali, conversioni di tipo,
costanti ed espressioni costanti,
direttive al preprocessore, inclusione
condizionale
2. Il concetto di tipo
• Determina i valori che può assumere una variabile e le operazioni
ammissibili su di essa
• Determina la memoria minima necessaria per memorizzare una
variabile
• int: di solito una parola; float: di solito due parole
• Permette al compilatore di rilevare errori nell'uso delle variabili
– un linguaggio è fortemente tipizzato se garantisce:
• correttezza delle istruzioni rispetto al tipo degli operandi, verificabile dal
compilatore
• che non sorgano errori di tipo in esecuzione
• Il C++ persegue la tipizzazione forte
– però è al tempo stesso molto permissivo, poiché ammette molte
conversioni automatiche tra tipi diversi
2
3. Il sistema dei tipi del C++
• Uno degli obiettivi principali del C++ è
permettere al programmatore di creare tipi
personalizzati e usarli come se fossero offerti
dal linguaggio
• Il sistema dei tipi del C++ comprende
– Tipi predefiniti (predefined)
– Tipi composti (composite)
– Tipi definiti dall’utente (user defined)
– Tipi classe (class types)
4. Cosa c’è da sapere di ogni tipo
• Che valori ammette
• Come sono rappresentati i valori nel calcolatore
– Sarebbe bello ignorarlo, ma talvolta non si può
• Come rappresentare valori (costanti) di quel tipo nel
programma (anche detti "literal")
• Come dichiarare e inizializzare variabili di quel tipo
• Quali operazioni sono ammesse sulle variabili
• Quali tipi sono “affini”
– Sono definite conversioni automatiche tra tipi affini
5. Inizializzazione delle variabili
• Le variabili si dichiarano e inizializzano
– La dichiarazione definisce il tipo
– L’inizializzazione definisce il valore iniziale della
variabile neonata
– L’inizializzazione NON è l’assegnamento
• quest’ultimo definisce il valore corrente della variabile
• Alcune cose che si possono fare durante
l’inizializzazione sono vietate nell’assegnamento
int i = 0;
tipo
identificatore inizializzatore
6. Inizializzazione di default
(tipi predefiniti)
• Una variabile dei tipi predefiniti priva di inizializzatore
viene inizializzata per default
• Il valore di default dipende dal tipo della variabile e
dalla posizione della dichiarazione
– Variabili dei tipi predefiniti definite al di fuori di una
funzione ricevono per default il valore 0
– Variabili dei tipi predefiniti definite all'interno di una
funzione sono uninitialized, cioè il valore NON è definito
• NB: per le variabili di tipo classe, il valore di
inizializzazione per default può essere specificato dal
programmatore
7. Check this out!
• http://stackoverflow.com/questions/18870547/why-there-isno-difference-between-initialization-and-assignment-for-thebuild-im/articles/article.aspx?p=376876
8. I tipi predefiniti del C++
• I tipi aritmetici: integral e floating point
9. Il tipo bool
• Sta per Boolean (booleano)
• Rappresentare il tipo delle espressioni che il
programma usa per prendere decisioni (se…,
fintantoché…)
• Ammette solo due valori: true e false
– false è rappresentato tramite il valore numerico 0
– true è rappresentato da qualunque valore numerico
diverso da 0
bool
bool
bool
bool
b
b
b
b
=
=
=
=
true; // b vale true
23; // b vale true
false; // b vale false
0; // b vale false
• In altre parole bool è un tipo (affine a) intero!
10. Il tipo char
• Ammette come valore un singolo carattere
• Utilizza il minimo numero di bit necessario a rappresentare
l’insieme dei caratteri standard del calcolatore un byte (di
solito 8 bit per rappresentare i caratteri ASCII)
• I caratteri sono rappresentati internamente da numeri interi e
quindi tra essi è definito un ordinamento
– Esempio: il carattere '1' è rappresentato dall’intero 49, il carattere
'A' dall’intero 65, …
• quindi, nell’ordinamento, risulta '1' < 'A'
– Per le lettere è scelta una codifica tale per cui l’ordinamento dei codici
coincide con l’usuale ordinamento alfabetico: 'A'<'B' ecc.
• Lettere alfabeticamente adiacenti hanno codifiche adiacenti
– Negli insiemi più usati le maiuscole latine sono codificate “prima”
delle minuscole
• Quindi ad esempio 'A'<'a' ma anche 'Z'<'a'
10
11. Il concetto di insieme di caratteri
• A differenza dei numeri
interi, i caratteri non hanno
una rappresentazione
“naturale” nel calcolatore
• La rappresentazione è
convenzionale e si ottiene
“numerando” i simboli
dell’insieme dei caratteri
che si vuole rappresentare
• Esistono molti insiemi di
caratteri, che coprono un
numero differente di
simboli di lingue diverse
• Gli insiemi di caratteri e
le codifiche più usati
sono
– ASCII
– ISO/IEC 8859
– UNICODE con UTF-8 e
UTF-16
13. Insieme di caratteri ASCII
• Lo American Standard Code for Information
Interchange (ASCII ) è una codifica dei caratteri basata
sull’alfabeto inglese che copre 27= 128 caratteri
• Usa 7-bit
• 95 caratteri stampabili: i numeri 0-9, le lettere a-z e AZ, simboli di punteggiatura e lo spazio bianco
• Vari caratteri non stampabili, tra cui alcuni codici di
controllo derivanti dalle Teletype machines
15. ISO 8859 (ISO LATIN)
•
•
•
•
I 95 caratteri ASCII stampabili
sono sufficienti per lo scambio di
informazioni in lingua inglese
Molte altre lingue che usano
l'alfabeto latino hanno bisogno di
simboli addizionali, come la ß
(tedesco), la ñ (spagnolo), la å
(svedese e altre lingue
scandinave) o le lettere accentate
italiane (à, è, é, ì, ò, ù)
L'ISO 8859 utilizza l'ottavo bit del
byte, che permette la codifica di
altri 128 caratteri
ISO 8859 è organizzato in diverse
parti: es: ISO 8859-1, Latin-1
Western European
http://en.wikipedia.org/wiki/ISO/IEC_8859
16. UNICODE, UTF-8, UTF-16
•
•
•
Unicode è un insieme di caratteri
e una sistema di codifica che
comprende oltre 110.000 simboli
Unicode si sovrappone a ISO/IEC
8859-1, ma codifica inoltre i
caratteri di quasi tutte le lingue
vive e in alcune lingue morte,
nonché simboli matematici e
chimici, cartografici, l'alfabeto
Braille, ideogrammi ecc.
I caratteri Unicode sono
rappresentati con le codifiche
UTF-8 e UTF-16
•
•
UTF-8 (8-bit Unicode
Transformation Format) è una
codifica dei caratteri Unicode
– usa un byte per i caratteri
dell’insieme ASCII, che hanno lo
stesso codice sia UTF-8 sia in
ASCII, e fino a 4 bytes per gli altri
caratteri (codifica a lunghezza
variabile)
UTF-16 (16-bit Unicode
Transformation Format) è una
codifica dei caratteri Unicode
– può rappresentare fino a
1,112,064 entità (code points) tra
cui i caratteri UNICODE da 0 a
0x10FFFF
– La codifica è a lunghezza
variabile, con uno o due unità da
16 bit
17. I tipi int
• Approssima il dominio degli interi
• Normalmente utilizza 1 parola di memoria
• Esistono anche le varianti
– short int, long int e long long int
– tipico: long 32 bit, long long 64 bit, short 16 bit,
e int 16 o 32 (per cui spesso int è grande quanto long
int)
• Lo standard del C++ richiede che
– spazio per short <= spazio per int <= spazio per long
<= spazio per long long
18. La gestione del segno
• TUTTI i tipi integral, tranne bool, ammettono la
versione “senza segno” (unsigned)
• Il tipo normale permette di rappresentare numeri
interi positivi e negativi
• Il tipo unsigned permette di rappresentare solo
numeri interi positivi, ma utilizza un bit in più (e
quindi può rappresentare di più valori)
– unsigned int: un valore intero positivo di almeno 16
bit, compreso tra 0 e 216-1
– Il prefisso signed si può omettere
– unsigned èquivale a unsigned int
– unsigned char da 0 a 255
19. Rapporto tra tipi signed e unsigned
• Tipi signed e unsigned non andrebbero mescolati
• Se si assegna un valore fuori intervallo a una variabile
di tipo unsigned, il risultato è definito come:
– Il resto della divisione del valore per il massimo intero che
il tipo unsigned può rappresentare
• Per esempio, un tipo unsigned char da 8-bit
rappresenta valori da 0 a 255.
• L’assegnamento di un valore fuori intervallo dà come
esito il resto di tale valore diviso per 256
– Assegnare –1 a un unsigned char da 8 bit produce il valore
255!
20. Check this out
int main() { // Assegna a unsigned valore negativo
unsigned char c;
// va da 0 a 255;
cout << "Inserire un numero negativo: " << endl;
int i;
cin >> i;
cout << "Valore inserito (intero originale): " << i << endl;
c=i; // assegno numero negativo a variabile char
cout << "Valore inserito (char): " << c << endl;
i=c; // recupero il valore intero di c
cout << "Valore inserito (intero dopo la conversione a char): "
<< i << endl;
return 0;
}
• Il compilatore non può rilevare l’errore
• Non sa predire che valore inserisce l’utente
• NB:
21. Rapporto tra char e int
• In molti linguaggi di programmazione, char e int sono
tipi non solo diversi, ma incompatibili
• C e C++ considerano i caratteri come “una particolare
interpretazione” dei numeri interi
• La convertibilità tra char e int può portare a
programmi difficili da comprendere
• Usare char solo quando serve davvero una variabile
che contenga un carattere, per esempio per
– Cercare/contare/sostituire tutte le occorrenze di un
carattere in un testo
22. Da char a int
• Determinare il codice di una lettera minuscola
• Cosa serve
– Una variabile char
– Una variabile int
• Algoritmo
1. Leggi il prossimo carattere
2. Se è una lettera maiuscola
1.
2.
3.
Convertilo in un intero
Stampa il risultato della conversione
Torna al punto uno
3. Termina
23. Da char a int
int main() {
// Legge un carattere e stampa il codice ASCII
char c;
// se è una minuscola; altrimenti termina
int i;
cout << "Scrivi un car. minuscolo (maiusc. x finire)" << endl;
cin >> c;
while (c >= 'a' && c <= 'z') { // minuscolo?
i = c; // assegna il valore del carattere a un intero
cout << "Valore ASCII per " << c << " è" << i << endl;
cout << "Scrivi car. minuscolo (maiusc. x finire)" << endl;
cin >> c;
}
return 0; // termina
}
NB: notare la ripetizione delle istruzioni che richiedono
l'input
24. Iterazione e condizione
• while (c >= 'a' && c <= 'z') {..
Corpo del ciclo..}
• Se la condizione è vera
• esegue le istruzioni all’interno del corpo
• Quando la condizione è falsa
• termina il ciclo (esce dal ciclo)
• (c >= 'a' && c <= 'z')
•
•
•
•
Esprime la condizione di permanenza nel ciclo
E’ un’espressione con valore di tipo bool, cioè vero o falso
&& Operatore logico AND
‘a’ è un valore costante (literal) di tipo carattere
25. Da int a char
int main() {
// Stampa il codice carattere
char c;
// corrispondente ai 128 codici ASCII
for (int i=0; i<128; ++i) {
c = i; // assegna il valore intero al carattere
cout << "Il carattere corrispondente a " << i
<< " è: " << c << endl;
}
return 0;
}
26. Iterazione a conteggio
• for (int i=0; i<128; ++i)
• Quando una iterazione deve essere svolta un
numero preciso di volte si può usare un “ciclo for”
• Il ciclo incrementa una variabile apposita (contatore)
per tener traccia del numero di interazioni
– int i=0; dichiarazione del contatore
– i<128; condizione di permanenza
– ++i avanzamento del contatore
27. Ciclo while e ciclo for
char c;
for (int i=0; i<128; i++) {
c = i;
cout << “Il carattere
corrispondente a " << i
<< " è: " << c << endl;
}
int i = 0;
char c;
while (i<128) {
c = i;
cout << “Il carattere
corrispondente a " << i
<< " è: " << c << endl;
i++;
}
28. I tipi float e double
• Approssimano i numeri razionali
• I valori si possono rappresentare con due notazioni
– 315.779
– 3.73E-5
• double di solito occupa più memoria di float
– tipicamente 4 byte per float e 8 byte per double
• (...esistono anche i long double, che devono occupare almeno
tanto spazio quanto i double)
• Lo standard prescrive la precisione minima, cioè il
minimo numero di cifre significative
28
29. Come rappresentare valori dei tipi
predefiniti
• Il valore 10 ha un tipo diverso da 10.0
• 10L è un valore di tipo long int
30. Cautele
• Attenzione ai confronti tra float
float a, b;
...
if (a == b) ...
• non ha senso, a causa delle approssimazioni nella
memorizzazione!!
diff = A - B;
if ((diff < EPSILON) && (-diff <
EPSILON));
• EPSILON costante definita in std::numeric_limits
31. Check this out
• http://stackoverflow.com/questions/17333/m
ost-effective-way-for-float-and-doublecomparison
32. Quali tipi scegliere
• Usare tipi unsigned quando si è certi che servono
solo valori positivi
• Usare int per valori aritmentici (short di solito
non basta)
• Se servono valori molto grandi usare long long
• Non usare char e bool in espressioni aritmentiche
– Se proprio proprio serve un intero piccolo usare signed
char o unsigned char
• Usare double per le operazioni in virgola mobile (di
solito float ha precisione insufficiente e le
operazioni sui double sono comunque veloci)
33. Conversioni di tipo
• Se un operando non è del tipo atteso e il compilatore
non ha informazioni sul tipo desiderato interviene
una conversione automatica di tipo
– È detta anche cast implicito
– Si opera la conversione anziché segnalare l'errore
• A volte è accompagnata da un “avvertimento” (warning)
• Esempio
– int i; float f;
– la valutazione dell’espressione i + f effettua prima la
conversione di i in float e poi la somma
35. Conversioni di tipo: esempi
int n, m;
float x,y;
n = 3.541 + 3; // il risultato è 6!
x = n;
x = n +
n = x;
n = n /
n = n /
x = n /
x = n /
n da “int” è promosso a “float”
x; // n è promosso a “float” e poi sommato a x
// x è troncato, ne sopravvive la parte intera
m; // il risultato della divisione tra interi è intero
x; /* n è convertito in “float”, poi si esegue la
divisione, il risultato è troncato a int */
x; //come sopra ma il risultato resta float
m; // attenzione: la divisione tra int tronca
//
36. Conversioni con il tipo bool
• Il tipo bool ha valore true o false
• I valori interi vengono convertiti a bool come segue
– 0 viene convertito a false
– ogni altro valore intero viene convertito a true
• Vale anche la conversione inversa (promozione a int)
bool b=false; int i; // inizializzazione
i = b; // i vale 0
b=true;
i = b; // i ora vale 1!
37. Conversioni con tipi un/signed
• Ecco perché è meglio non mescolare
unsigned u = 10;
int i = -42;
cout << i + i << endl; // prints -84
cout << u + i << endl; // if 32-bit ints, prints
4294967264
• Il valore di i (-42) viene convertito a unsigned
prima dell’addizione
unsigned u1 = 42, u2 = 10;
cout << u1 - u2 << endl; // ok: result is 32
cout << u2 - u1 << endl; // ok: but the result is… ?
38. Quando avvengono le conversioni
automatiche
• Nelle espressioni, i valori di tipi aritmetici piccoli vengono
solitamente promossi a int
char c, int i;
i=c+i; // da evitare
• Nelle condizioni, tipi non bool sono convertiti a bool
– while (33) equivale a while (true) !
• Nell’inizializzazione, il valore dell’inizializzatore è convertito al
tipo della variabile inizializzata
– int i = 3.14; // i inizializzato a 3 !
• Nelle espressioni aritmetiche, gli operando sono convertiti a
un tipo comune
• Nelle chiamate di funzione (che vedremo)
39. Altri esempi
bool b = 42; // b is true
int i = b; // i has value 1
i = 3.14; // i has value 3
double pi = i; // pi has value 3.0
// assuming 8-bit chars
unsigned char c = -1; // c has value 255
40. Conversione (cast) esplicita
• Quando una conversione utile non verrebbe
eseguita, la si può richiedere esplicitamente
• Es: divisione tra interi con risultato double
x = (double) n / m;
• ES: troncamento esplicito del risultato
cout << (int) x / y;
• C++11 introduce una sintassi più precisa
41. ATTENZIONE
• Il cast (implicito o esplicito che sia) non modifica il
tipo della variabile o delle variabili coinvolte, ma solo
il tipo associato al valore dell'espressione
• Le variabili in memoria continuano a essere del tipo
dichiarato staticamente nella parte dichiarativa del
programma
• Il cambio del tipo della variabile può portare al
cambio del valore dell’espressione dove la variabile è
usata
42. Le espressioni
•
Espressione: applicazione di operatori a
operandi
– Ha un valore e un tipo
– Condizione: espressione con tipo booleano
•
Cosa bisogna sapere sulle espressioni
1. Ordine di valutazione
2. Proprietà degli operandi (precedenza,
associatività)
3. Tipo degli operatori e del risultato, conversioni
43. Espressioni base
Le espressioni base si compongono di:
• Variabili
– Il tipo è noto in base alla dichiarazione, e NON cambia
• Valori (detti anche costanti o literal)
– Il tipo è deducibile da come sono “scritte”
•
•
•
•
•
•
•
3 : int (il più piccolo tra int, long, long long in cui il valore ci sta)
03 : int in notazione ottale
0x3 : int in notazione esadecimale
3.0 : float
3.0L : long
'3' : char
"3" : stringa (sequenza di caratteri di lunghezza variabile)
44. Constanti di tipo stringa
"Hello World!" // string literal
•Ha un tipo composto che vedremo in seguito
– Array di const char
•Il compilatore appende al valore un carattere
speciale "terminatore" ('0')
•La dimensione reale del valore è 1 + la dimensione
apparente (c'è anche il carattere "terminatore")
•Attenzione: "a" ha tipo e dimensione diversi da 'a'
45. Espressioni e operatori
• Operatori unari: sia applicano a un operando
– -2
• Operatori binari: sia applicano a due operandi
– a+b
• Il valore di un'espressione dipende da
precedenza e associatività degli operatori e può
dipendere dall'ordine di valutazione delle sottoespressioni
– Quanto vale : 5 + 10 * 20/2 ?
• Nella valutazione intervengono anche le
conversioni di tipo
46. Precedenza e associatività
•
•
•
•
•
Un'espressione con due o più operatori di dice composta
La sua valutazione richiede di assegnare gli operandi agli operatori
Si usano le regole di precedenza tra operatori diversi e le regole di
associatività per operatori con la stessa precedenza
– Precedenza: 3+4*5 vale 23, non 35
– Associatività: 20-15-3 vale 2, non 8 (associatività a sinistra)
Le parentesi possono cambiare le regole
– (3+4)*5 vale 35
– 20-(15-3) vale 8
Non solo operatori aritmetici:
–
–
–
–
cin >> v1 >> v2; // read into v1 and then into v2
L'operatore >> è associativo a sinistra
Il valore dell'espressione cin >> v1 è cin !
Equivale a (cin >> v1) >> v2;
47. Ordine di valutazione operandi
• In generale l'ordine di valutazione di sotto-espressioni con
la stessa priorità NON è definito
– int i = f1() + f2(); // f1, f2 sono funzioni
• Non si sa quale tra le funzioni f1() e f2() venga valutata per
prima
• Non si devono fare ipotesi indebite
– int i = 0;
– cout << i << " " << ++i << endl; // undefined
– ++i espressione che incrementa i e "torna" il nuovo valore
– Potrebbe stampare 1 1 oppure 0 1 o qualsiasi altra cosa
• Ci sono 4 operatori per cui invece è stabilito l'ordine di
valutazione delle sotto-espressioni
– AND logico (&&) OR logico (||) operatore condizionale (? :),
operatore virgola (,)
48. Operatori aritmetici
• Precedenza e associatività (tutti associativi a sinistra)
• PS: % indica il resto delle divisione tra due interi
– Se m e n sono interi e n è diverso da 0,
• (m/n)*n + m%n = m
– Non si applica a tipi in virgola mobile
precedenza
49. Operatori logici e relazionali
• Sono usati per costruire condizioni, cioè espressioni
con valore di tipo bool
precedenza
50. La regola del cortocircuito
• Gli operatori AND e OR in C e C++ hanno un ordine di
valutazione prefissato (a differenza delle pure
espressioni della logica)
– Prima l’operando di sinistra POI quello di destra
• Vale una regola detta del cortocircuito che evita di
valutare inutilmente l’operando di destra
– AND: se l’operando di sinistra vale false
– OR: se l’operando di sinistra vale true
• La regola del corto circuito ha applicazioni
interessanti, per esempio quando si ispezionano
sequenze di dati (lo vedremo in seguito)
51. L’operatore di assegnamento
• Anche = è un operatore!
• L’operando di sinistra deve essere una variabile (più
precisamente un lvalue), quello di destra un’espressione (più
precisamente un rvalue)
• Modifica lo stato del programma: assegna il valore
dell’operando di destra all’operando di sinistra
–
–
–
–
i=3 è un’espressione, il cui valore è l’operando di sinistra, cioè i!
i=3; è un’istruzione! Per via del ;
L’operatore = è associativo a destra
x=y=3 equivale a x=(y=3)
• Si assegna il valore 3 a y, il risultato dell’espressione è y
• Si effettua l’assegnamento x=y che copia il valore di y in x, anche x vale 3
• L’inizializzazione NON è un assegnamento (mai ribadito
abbastanza)
52. Esempi
int i = 0, j = 0, k = 0; // initializations, not
// assignment
1024 = k; // error: literals are rvalues
i + j = k; // error: arithmetic expressions are rvalues
k = 0; // result: type int, value 0
k = 3.14159; // result: type int, value 3
53. ATTENZIONE
• Possiamo usare assegnamenti come condizioni e viceversa?
• Si, perché = è un operatore, proprio come ==
• Di solito però… succede quando si sta commettendo un
errore...!
– x = y == 3;
– while ( 3 = x )
• Ma soprattutto...
// Assegna a x il valore 0 o 1 e non modifica
// Errore di sintassi (3 non è una variabile)
– while(x = 3) ... È SEMPRE VERO!!! (per ogni valore precedente di x)
– while (x = 0) ... È SEMPRE FALSO!!! (per ogni valore precedente di x)
– while (x = y) ... equivale a scrivere
• x = y;
• while ( y != 0 ) ...
• Ancora peggio...
– while (! (x = 0)) ... NON TERMINA MAI !!!!
54. I costruttori di tipi
• A partire dai tipi predefiniti si possono creare
nuovi tipi, che aggiungono proprietà speciali ai
tipi predefiniti
–
–
–
–
const
reference
pointer
array
• A questi si aggiungono costruttori di tipi definiti
interamente dall'utente
– class
– struct (eredità del C, poco usato in C++)
55. Il qualificatore const
• Talora serve definire una variabile che non cambi valore
• Per farlo si premette const alla dichiarazione
– const int modelloauto = 159; // tipo const int
– const float pigreco = 3.14159265; // tipo const float
• Un tipo const è diverso dal corrispondente tipo base
• Una variabile const
– Si dichiara e inizializza obbligatoriamente
– Non ammette assegnamento
•
•
•
•
•
int i = 42;
const int ci = i; // ok: the value in i is copied into ci
ci = 43; // error, no assignment
const int k; // error: k is uninitialized const
int j = ci; // ok: the value in ci is copied into j
56. Visibilità delle variabili const
• const float pigreco = 3.14159265;
• Il compilatore sostituisce pigreco con il valore
associato
• Il compilatore deve conoscere il valore
dell'inizializzatore
• Se il programma è diviso in più file, la variabile
const viene (ri)definita in ciascun file
• Alternativamente, la variabile const può essere
definita in un solo file e dichiarata come esterna
nei file che ne fanno uso
57. extern const
• Per definire una variabile const una sola volta, si usa la keyword
extern sia nella definizione che nelle dichiarazioni
// file_1.cc definisce e inizializza una variabile const
extern const float pigreco = 3.14159265;
// file_1.h
extern const float pigreco; // stessa const di file_1.cc
• file_1.cc definisce e inizializza pigreco. Siccome la dichiarazione
comprende un inizializzatore è una definizione. Però, siccome
pigreco è const, si specifica extern per farla usare in altri file.
• La dichiarazione in file_1.h è anch'essa extern. Significa che pigreco
non è locale a tale file ma definita altrove
• Altri file possono includere il file file_1.h e importare la definizione
di pigreco
58. Definizione di macro: #define
• Esiste anche un altro modo di definire un valore una volta per tutte
– la direttiva #define
• #define PIGRECO 3.141592
– #define è una direttiva al preprocessore (come #include)
– Non è terminata dal punto e virgola
• una direttiva al precompilatore non è un’istruzione C++
– Non causa allocazione di memoria
• PIGRECO è una "costante simbolica"
– Il simbolo PIGRECO viene sostituito nel codice con il valore 3.14 …
prima che il programma sia compilato
– Si dice che PIGRECO è una macro-definizione (o semplicemente una
MACRO)
– Per convenzione, le costanti definite tramite macro sono interamente
maiuscole (es: TIPICA_COSTANTE)
59. Definizione di macro: #define
• #define MIGLIO 1609.34
• #define YARDA
0.9144
• Si possono costruire MACRO a partire da altre
MACRO, e una macro può anche usare uno o
più parametri:
– #define AREACERCHIO(X) (PIGRECO∗(X)∗(X))
– area = AREACERCHIO(4);
– Diventa ⇒ area = (3.141592∗(4)∗(4));
– ma è un valore fisso !!
60. Altre direttive al precompilatore
• Abbiamo già visto:
#include <nome_libreria>
– Serve a richiamare librerie
– comanda al preprocessore di leggere anche da un altro file
sorgente
• Altra direttiva usata frequentemente:
#undef VERO
– Serve ad annullare la definizione di una MACRO
– Da quel punto in poi la costante VERO non è più definita
61. Esempio di programma multifile
/* File contenente funzioni di utilità sui cerchi
* dichiarazione e definizione di una costante
* riusabile : pigreco.cpp */
extern const float pigreco = 3.14159265;
float area(float r){
return r*r*pigreco;
}
float perimeter(float r){
return 2*r*pigreco;
}
62. File di intestazione della libreria
/*
*
*
*
*
File contenente solo le dichiarazioni delle
funzioni di utilità sui cerchi
e della costante riusabile: pigreco.h
Usato dal compilatore per controllare la
correttezza del programma */
extern const float pigreco;
float area(float r);
float perimeter(float r);
63. Programma principale
#include <iostream>
#include "pigreco.h"
using namespace std;
int main() {
float radius;
cout << "Insert the value of the radius: " << endl;
cin >> radius;
cout << "Value of pigreco: " << pigreco << endl;
cout << "Perimeter of circle: " << perimeter(radius) <<
cout << "Area of the circle: " << area(radius) << endl;
return 0;
}
endl;
64. I tipi definiti dall'utente
• La ragione principale del successo di C++ è che
consente di estendere il sistema dei tipi in
modo assai potente
• La novità introdotta dal C++ è che i tipi definiti
dall'utente si possono usare (quasi) come
quelli predefiniti
65. Requisiti
• Si vuole costruire un programma che gestisca le
vendite di libri
• Ogni libro è caratterizzato da un codice ISBN
• Il programma deve gestire per ogni libro il numero
totale di copie vendute, il ricavo totale e medio
• L'utente inserisce sequenzialmente i dati di vendita per
i diversi libri, il programma aggrega i dati per ciascun
codice ISBN e stampa il dato aggregato
• Deve quindi essere possibile:
– Inserire e stampare i dati di un libro
– Sommare più dati di vendita relativi allo stesso libro
– Stampare i dati di vendita aggregati relativi a un libro
66. Progettazione
• Inizializza il totale vendite a 0
• Leggi dati 1° transazione e aggiorna il totale
• Finché ci sono transazioni
– Leggi un'altra transazione;
– Se la transazione letta è dello stesso libro di prima
• sommala al totale corrente;
– Altrimenti
• stampa il totale corrente; // è cambiato il libro
• inizializza il totale corrente del nuovo libro coi dati della
transazione letta;
•
Stampa il totale di vendita dell'ultimo libro (perché
serve??)
67. Suddivisione del programma
• Un file di libreria (Sales_item.h) conterrà la
definizione del tipo che rappresenta il dato di
vendita: Sales_item
• Sales_item è un tipo classe, cioè un tipo
definito dall'utente che comprende dati e
operazioni relativi al dato di vendita
• Il programma principale (Booksales.cpp)
importa e usa Sales_item come se fosse un
tipo predefinito
– Sales_item item; // dichiara una var. di tipo Sales_item !
68. Un primo esempio di main()
#include <iostream>
#include "Sales_item.h"
int main()
{
Sales_item book;
// read ISBN, number of copies sold, and sales price
std::cin >> book;
// write ISBN, # of copies sold, total revenue, and average price
std::cout << book << std::endl;
return 0;
}
•book è trattata come una variabile di un tipo predefinito
•Si applicano gli stessi operatori di I/O che a una variabile di tipo int
•Gli operatori sono definiti nel tipo classe Sales_item
69. L'esempio completo
int main()
{
Sales_item total; // variable to hold data for the next transaction
// read the first transaction and ensure that there are data to process
if (cin >> total) {
Sales_item trans; // variable to hold the running sum
// read and process the remaining transactions
while (cin >> trans) {
// if we're still processing the same book
if (total.isbn() == trans.isbn())
total += trans; // update the running total
else {
// print results for the previous book
cout << total << endl;
total = trans; // total now refers to the next book
}
}
cout << total << endl; // print the last transaction
} else {
// no input! warn the user
cerr << "No data?!" << endl;
return -1; // indicate failure
}
return 0;
}
if(cin >> x)
while(cin >> x)
La condizione è
soddisfatta se
l'acquisizione dell'input
non ha dato errori
Per terminare, basta
premere CTRL-Z
(carattere end of file)