Elementi di programmazione ad oggetti a. a. 2009/2010
description
Transcript of Elementi di programmazione ad oggetti a. a. 2009/2010
Elementi di programmazione
ad oggetti
a. a. 2009/2010
Corso di Laurea Magistrale in Ingegneria ElettronicaDocente: Mauro Mazzieri, Dipartimento di Ingegneria Informatica, Gestionale e dell’Automazione
Programma del corso1. Introduzione al linguaggio C++ e alla libreria
standard; richiami di programmazione procedurale.2. Regole di visibilità e ciclo di vita.3. Funzioni e sovraccaricamento.4. Introduzione alla progettazione e programmazione
ad oggetti: classi ed oggetti, notazione UML.5. Sovraccaricamento degli operatori.6. Ereditarietà. Funzioni virtuali e polimorfismo.7. Template.8. Gestione delle eccezioni.9. Input/output e stream.10. La libreria standard: i contenitori; cenni su oggetti
funzione, algoritmi, iteratori, allocatori, stringhe, calcolo numerico.
11. Unit testing e test-driven programming.
Testi di riferimento Lippman, Lajoie, "C++ Corso di
Programmazione", Addison-Welsey consigliato
Stroustrup, "C++ Linguaggio, libreria standard, principi di programmazione", Addison-Wesley La bibbia del C++, consigliato a chi sa già
programmare (in altri linguaggi) e desidera la fonte più autorevole sul C++
… o qualsiasi altro libro di C++ Aguilar, “Fondamenti di programmazione in C+
+”, McGraw-Hill Schildt, "Guida Completa al C++", Mc Graw-Hill Deitel, Deitel, "C++ Fondamenti di
Programmazione", Apogeo
Alcune note Le slides del corso saranno messe
al più presto a disposizione sul portale del DIIGA Le slides non sono dispense, occorre
seguire le lezioni e/o leggere uno dei libri di riferimento, ed esercitarsi all’elaboratore
Quale ambiente di sviluppo per C++? Windows: DevC++, Visual C++
Express Mac: XCode Linux: KDevelop
Lezione 1Introduzione al linguaggio C++ ed alla libreria standardRichiami di programmazione procedurale
Introduzione al C++
Bjarne Stroustrup ha inventato il C++ Eredita dal C le caratteristiche di
basso livello Altre fonti di ispirazione: Simula67,
Algol68, Ada, Clu Il primo “C con le classi” è del
1980, i primi usi fuori dagli ambienti di ricerca sono del 1983
Standard ISO nel 1998
Cos’è il C++?
Il C++ è un linguaggio di programmazione general-purpose orientato alla realizzazione di sistemi È un C migliorato Supporta l’astrazione dei dati Supporta la programmazione
orientata agli oggetti Supporta la programmazione
generica
Programmazione procedurale La programmazione procedurale si
basa sull’individuazione delle procedure che occorrono e utilizzo dei migliori algoritmi possibili Decomposizione successiva del
sistema da implementare in funzionalità più semplici
Ci si ferma quando le funzionalità sono sufficientemente semplici da essere implementabili come funzioni
Esempio di programmazione procedurale#include <iostream>
using namespace std;
void scambia(int& n1, int& n2) {int temp = n1;n1 = n2;n2 = temp;
}
int main(){
int a, b;cout << "a=";cin >> a;cout << "b=";cin >> b;scambia(a, b);cout << "a=" << a << ", b=" << b << endl;system("pause");return 0;
}
Alcune note sul programma#include <iostream> Direttiva per il preprocessore, indica che il
programma ha la necessità di usare le funzioni della libreria predefinita per la gestione dell’I/O
using namespace std; Gli identificatori possono avere un prefisso
(“spazio dei nomi”); tramite questa direttiva è possibile usare le funzioni di libreria omettendo il prefisso std
cout << "a="; Output di una stringa sullo standard outputcin >> a; Input di una stringa dallo standard input
Programmazione modulare Un insieme di procedure
correlate e di dati da esse manipolati costituisce un modulo
La programmazione modulare si basa sulla suddivisione del programma in moduli, in modo che i dati siano nascosti all’interno dei moduli
namespace Dati, funzioni ed altre entità correlate possono essere
correlati in spazi di nomi (namespace) separati
// interfaccianamespace stack { void push(int); int pop();}
// implementazionenamespace stack { const int MaxSize = 100; int data[MaxSize]; int top = 0; void push(int i) { // controlla che la pila non sia piena ed inserisce i in cima }
int pop() { // controlla che la pila non sia vuota e restituisce l’elemento
in cima }}
Compilazione separata stack.hnamespace stack { void push(int); int pop();} stack.cpp#include “stack.h”
namespace stack { const int MaxSize = 100; int data[MaxSize]; int top = 0;}
void stack::push(int i) { // controlla che la pila non sia piena ed inserisce i in cima }
int stack::pop() { // controlla che la pila non sia vuota e restituisce l’elemento
in cima }
Progettazione orientata agli oggetti Si individuano le classi di oggetti
che caratterizzano il dominio applicativo Entità reali o astratte
Si individuano le modalità secondo cui gli oggetti devono interagire per realizzare le funzionalità dell’applicazione
Ogni classe è descritta da un’interfaccia che specifica il comportamento degli oggetti della classe
Classi e oggetti Un oggetto è una istanza di una classe di oggetti che condividono lo stesso comportamento Lo stato di un’istanza è indipendente
dallo stato delle altre istanze Un oggetto rappresenta un’entità del
mondo reale o astratta Un oggetto è caratterizzato da un nome,
da dati (variabili locale che ne descrivono lo stato) e metodi (funzioni che ne descrivono i comportamento)
Gli oggetti dialogano tra loro scambiandosi messaggi
Programmazione generica La programmazione generica
consente di parametrizzare gli algoritmi che occorrono in modo che funzionino per un’adeguata varietà di tipi e strutture dati Contenitori Algoritmi generici
Richiami di programmazione procedurale
Tipi di dato Tipi di dato primitivi:
int char float, double bool enum
Tipi composti a partire dai tipi primitivi: struct Puntatori Vettori
Variabili Per memorizzare ed utilizzare un dato
è necessario dichiararne il tipo ed il nome
Le locazioni di memoria dell’elaboratore contengono un dato La locazione di memoria è indentificata
da un indirizzo Il contenuto della locazione di memoria è
il valore
int a = 2; dichiara il tipo ed inizializza il valore
char b; Dichiara il tipo ma non inizializza il valore
Operatore di assegnamento L’operatore di assegnamento: =
All’elemento a sinistra dell’operatore (Lvalue) deve poter essere cambiato il valore
Solitamente, variabili Dell’elemento a destra (Rvalue) è
importante solo il contenuto Variabili, numeri, output di funzioni
Un Lvalue può fungere da Rvalue ma non viceversa
Operatore di uguaglianza: ==
Operatori Aritmetici
+ addizione - sottrazione * moltiplicazione / divisione % resto (modulo)
Logici && AND || OR ! NOT
Relazionali > maggiore < minore >= maggiore o
uguale <= minore o
uguale == uguale != diverso
Identificatori
Gli identificatori sono i nomi usati per rappresentare variabili, costanti, tipi, funzioni
Un identificatore viene dichiarato specificandolo della dichiarazione Sequenza di caratteri (lettere,
numeri, _), il primo deve essere una lettera o _
Parole chiave Non possono essere usate come
identificatori!asm, auto, bool, break, case, catch, char, class, const, const_cast, continue, default, delete, do, double, dynamic_cast, else, enum, explicit, export, extern, false, float, for, friend, goto, if, inline, int, long, mutable, namespace, new, operator, private, protected, public, register, reinterpret_cast, return, short, signed, sizeof, static, static_cast, struct, switch, template, thwows, true, try, typedef, typeid, typename, union, unsigned, using, virtual, void, volative, wchar_t, while
Strutture di controllo: if
if (espressione) {// codice che viene eseguito solo se l’espressione è true
} else {// codice che viene eseguito solo se l’epsressione è false
}
Strutture di controllo: while e dowhile (espressione) {
// istruzioni che vengono eseguite fintanto che l’espressione è true
}
do {// istruzioni che vengono eseguite almeno una volta e fintanto che l’espressione è true
} while (espressione)
Strutture di controllo: for
for (espr1; espr2; espr3) {// istruzioni eseguite fintanto che espr2 è true
}
Espr1 viene valutata una volta sola, prima di iniziare
Espr2 si valuta prima delle istruzioni
Espr3 si valuta dopo le istruzioni
Puntatore
Il puntatore è un tipo di dato derivato, che contiene l’indirizzo di una locazione di memoria capace di contenere valori di un certo tipo
int *a;*a = 3; * è l’operatore di deferenziazione int *a si legge “il contenuto della
locazione di memoria è un intero” (dunque, a è un puntatore)
Riferimenti Un riferimento fornisce un nome
alternativo ad un elementoint a=5;
int &b = a; Un riferimento deve essere
inizializzato contestualmente alla sua dichiarazione
L’operatore & fornisce l’indirizzo di una variabile:int *c;
c = &a;
Array
int a[5] = { 5, 10, 15, 20, 25 }
int *p; Il nome di un array è un
puntatore al primo elemento:p = a; oppurep = &a[0]
Puntatori const Il qualificatore const indica che un
elemento non può essere cambiatoconst int a = 5;
È obbligatorio inizializzare una costante nel momento in cui viene definita
Un puntatore const punta sempre alla stessa locazione di memoria
int a = 5;const int b =6;const int* c = &a;const int* d = &b;int* d = &b;
Allocazione della memoria L’operatore new alloca a run-time
della memoria La memoria allocata a run-time risiede in
un’area apposita chiamata heapint *a = new int;
a contiene un indirizzo corrispondante ad una locazione dell’heap
a viene solo allocata, non inizializzata int *a = new int(2); // a viene anche inizializzata
int *b = new int[2]; // allocazione di un vettore
b[0] = 3;b[1] = 4;
Rilascio della memoria L’operatore delete libera della memoria
dinamica allocata con newint *a = new int;…delete a;
la memoria all’indirizzo a può essere riallocata la memoria non viene cancellata a non cambia valore
Dopo il delete, a è un puntatore dangling (punta ad una locazione di memoria non valida)
Errori comuni Memory leak: non rilasciare con delete memoria
allocata con new Applicare due volte delete alla stessa locazione di
memoria Usare l’oggetto dopo il delete
Allocazione dinamica di arrayint *p1 = new int(24); // alloca un solo
intero, inizializzandolo a 24int * p2 = new int[24]; // alloca un vettore di
24 interi, non inizializzatiint (*p3)[1024] = new int[4][1024]; //alloca
una matrice di 4 x 1024 interi Non si possono inizializzare gli elementi di
un array alla loro allocazione, occorre un ciclo for
int a = 1024;int *p = new int[a];for (int i = 0; i < a; i++)
p[i] = 1; Un array si rilascia con delete[]delete[] p;
Allocazione dinamica const Una variabile const una volta
inizializzata non può essere modificata, anche se creata sull’heap
const int *a = new const int(23);
Una variabile allocata dinamicamente const Deve essere sempre inizializzata L’indirizzo di memoria restituito deve
essere assegnato a un puntatore const
Funzioni Operazione definita dall’utente
Gli operandi sono i parametri Il risultato è il valore di ritorno
Il tipo del valore di ritorno è il tipo di ritorno della funzione
void se non restituisce nulla
Le azione svolte sono contenute nel blocco della funzione
Tipo di ritorno, nome della funzione e lista dei parametri costituiscono la definizione della funzione
Parametri e variabili locali Le variabili definite nel corpo di
una funzione sono note solo all’interno della funzione
I parametri sono variabili locali istanziate alla chiamate della funzione
Prototipo
Il prototipo di una funzione è costituito dalla dichiarazione di tipo di ritorno, nome e parametri (senza il corpo)
int middle(int, int, int); E’ necessario definire il prototipo
solo quando la definizione avviene dopo la chiamata della funzione
Passaggio di parametri Il C++ è fortemente tipizzato: il tipo
degli argomenti è controllato dal compilatore
La maniera predefinita di passare gli argomenti è per valore vengono copiati nello spazio di memoria
dei parametri è un problema passare argomenti molto grandi
la funzione manipola solo delle copie locali
non possono essere modificati i valori degli argomenti
Introduzione alla libreria standard
Uso delle funzioni della libreria standard Per utilizzare una funzione della
libreria standard, bisogna includere l’intestazione in cui sono definite:#include <iostream>
using namespace std;
[…]
cout << “hello!”;
Flussi di input/output
#include <iostream>Using namespace std;[…]cout << “a=”;int a;cin >> a;cout << a;cout << endl;cout << “Ripeto: il valore di a è” << a << “!”;
Stringhe
string s1 = “benvenuto”;string s2 = s1 + “!\n”;bool affermativo(const string &risposta) {return risposta == “sì”;
}string s3 = s1.substr(5, 4); // nuto
string s4 = s1.replace(0, 5, “mal”); // malvenuto
La stringa sostituita può non avere la stessa lunghezza del sostituto!
Input di stringhe
string s;
cin >> s; // inserendo “Mauro”..
cout << “Ciao “ << s; // si ottiene “ciao Mauro”
Per leggere una riga intera:getline(cin, s);
Contenitori: vector Un array ha dimensione fissaint numeri1[100]; Un vettore ha dimensione variabilevector<int> numeri2(100);cout << numeri2.size(); \\ 100cout << numeri2[40]; \\ si accede ad un elemento come ad un array
numeri2.resize(200);cout << numeri2.size(); \\ 200vector numeri3[300]; \\ 300 vettori vuoti!!
Contenitori: list Una lista è adatta quando inserimenti e cancellazioni sono
frequentilist<double> valori; Di solito non si accede alla lista con un indice, ma si
scorrono i suoi elementifor (list<int>::const_iterator i = valori.begin(); i
!= valori.end(); i++)cout << *i;
for (list<int>::iterator i = valori.begin(); i != valori.end(); i++)*i = 1.0; Un iteratore non è un semplice puntatore, però
Con ++ si punta all’elemento successivo Con * si accede al contenuto
Aggiungere elementi ad una lista:valori.push_front(2.0); // inserimento in testavalori.push_back(3.0); // inserimento in codavalori.insert(i, 4.0); // inserimento prima
dell’elemento a cui i si riferisce
Altri contenitori map: array associativo (dizionario)
map<string, int> rubrica[…]cout << rubrica[“Mauro”];
queue: coda stack: pila deque: coda bidirezionale priority_queue: coda ordinata set: insieme multiset: insieme con valori ripetuti multimap: array associativo con valori
ripetuti
Algoritmi La libreria standard fornisce gli algoritmi più comuni
che operano su elementi dei contenitori standard Gli algoritmi operano su sequenze, determinate da
coppie di iteratorivector<int> v;list<int> l;[…]sort(v.begin(), v.end()); // ordinamentocopy(v.begin(), v.end(), l.begin()); // copiacopy(v.begin(), v.end(), back_inserter(l)); //
copia in coda, estendendo la lista quanto serve
int::const_iterator i = find(l.begin(), l.end(), 2); // restituisce un iteratore che punta all’elemento trovato
int c = count(v.begin(), v.end(), 3); // conta i 3 nel vettore
Algoritmi della libreria standardfor_each() Invoca una funzione per
ogni elemento
find_if() Trova il primo elemento che soddisfa la condizione
count_if() Conta gli elementi che soddisfano la condizione
replace_if() Sostituisce un elemento che soddisfa una condizione
unique_copy()
Copia elementi non duplicati
merge() Combina sequenze ordinate