View
39
Download
0
Category
Preview:
DESCRIPTION
Elementi di programmazione ad oggetti a. a. 2009/2010. Corso di Laurea Magistrale in Ingegneria Elettronica Docente: Mauro Mazzieri, Dipartimento di Ingegneria Informatica, Gestionale e dell’Automazione. Lezione 10.1. La libreria standard: i contenitori. Contenitori. - PowerPoint PPT Presentation
Citation preview
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
Contenitori Un contenitore è un oggetto che
serve a contenere altri oggetti Nei contenitori sequenziali ogni
elemento ha un indice numerico e si può accedere ad un elemento tramite la sua posizione
L’ordine degli elementi non dipende dal loro valore
Nei contenitori associativi l’ordine degli elementi dipende dal valore di una chiave
Gli oggetti contenuti sono tutti dello stesso tipo
Contenitori della libreria standard I contenitori sono definite come
template di classe Per usare un contenitore bisogna
includere il suo header, es.#include <vector>#include <list>
Contenitori sequenziali vector
Vettore con dimensione variabile Ottimizzato rispetto all’accesso indicizzato agli elementi
list Lista con collegamenti bidirezionali Ottimizzata rispetto all’inserimento e cancellazione di
elementi Non consente di accedere ad un elemento tramite un
indice, sarebbe troppo lento deque
Coda bidirezionale (double-ended queue) Adattatori:
stack Pila (LIFO)
queue Coda (FIFO)
priority_queue Coda con gestione della priorità
Vector
Il vector è il più semplice contenitore sequenziale
Definito nell’header vector#include <vector>
Un vector è definito come template di classe, pertanto va istanziato con un tipo: vector<int> ivec; vector<string> svec; vector<Impiegato> impiegati;
Vector: costruttori vector<T> v1;
Vuoto È il costruttore più usato
vector<T> v2(v1); Costruttore per copia
vector<T> v3(5); Contiene 5 elementi di tipo T Gli elementi sono inizializzati al loro valore
standard (es. 0 per gli interi) o tramite il loro costruttore di default
Se non c’è un costruttore di default per T, non si può usare questo costruttore per vector<T>
vector<T> v4(7, i); Contiene 7 elementi di tipo T inizializzati a i
i deve essere di tipo T vector<T> v5(i1, i2);
Inizializza il vettore con una copia degli elementi compresi tra l’iteratore i1 e l’iteratore i2
Dimensione di vector I vector sono concepiti per avere una
dimensione che cresce dinamicamente, man mano che si aggiungono oggetti
Sono l’equivalente dinamico degli array monodimensionali
È possibile avere un vettore di vettori, es.vector< vector<int> > v; È indispensabile lo spazio attorno al
parametro di tipo vector<vector<int>> v;causerebbe un errore di compilazione, >> è un operatore!
Operazioni sui vettori v.empty( ) v.size( )
Numero di elementi Il suo tipò è vector<T>::size_tipe
v.push_back(t) Aggiunge t in coda
v[n] Restituisce l’elemento in posizione n È un Lvalue Non avviene nessun controllo sull’indice!!
La funzione membro at() invece solleva un’eccezione di tipo out_of_range se il parametro supera la dimensione del vector
v1 = v2 Operatore di assegnamento
v1 == v2 (! =, < , <=, >, >=) Operatori di confronto Si può usare un operatore relazionale solo se lo stesso
operatore è definito anche per il tipo dell’elemento
Iteratori Un iteratore definisce un metodo
generale per accedere in successione a ciascun elemento di un contenitore
Gli iteratori ridefiniscono l’operatore di deferenziazione *, il che consente di usarli come fossero puntatori *i è un Lvalue (es. *i = t; è valido) e serve
ad accedere all’elemento i++ è l’iteratore si riferisce all’elemento
successivo i==j è vero se i e j si riferiscono allo
stesso oggetto
Iteratore Tutti i contenitori della libreria standard
definiscono un iteratore vector<T>::iterator i;
Gli iteratori ridefiniscono gli operatori deferenziazione *, consente l’uso come un puntatore Incremento ++ Uguaglianza ==, diuguaglianza != Operatore freccia, es. i->m dereferenzia i e
restituisce il membro m v.begin()
Restituisce un iteratore che si riferisce al primo elemento
v.end() Restituisce un iteratore che si riferisce ad una
posizione dopo l’ultimo elemento Non è un elemento valido, ma solo un valore
sentinella Se il vector è vuoto, v.begin() e v.end() coincidono
Com’è fatto un iteratore? Un modo ragionevole di rappresentare un
iteratore per un vettore è usare un puntatore ma potrebbe essere anche definito come una coppia
(puntatore, offset), che consentirebbe di controllare per una lista potrebbe essere un puntatore ad un
collegamento Al di là della loro effettiva definizione, ciò che
conta è che gli iteratori hanno degli operatori con una semantica comune
N.B. I puntatori sono utilizzabili iteratori su vettori! Es.int numbers[] = { 1, 3, 5, 7, 11 };size_t nnumbers = sizeof(numbers) / sizeof(int);vector<int> number_vector(numbers, numbers + nnumbers);
Aritmetica degli iteratori Per gli iteratori è definita un’aritmetica,
come per i puntatori i++, i--
Si riferisce all’elemento successivo o precedente Sono definiti per gli iteratori bidirezionali
i + 5, i -3, i += 7, i -= 9 Un certo numero di elementi più avanti o più
indietro Sono definiti per gli iteratori ad accesso casuale
i – j È di tipo vector<T>::difference_type A differenza di vector<T>::size_type, è un valore
con segno È definita solo per gli iteratori ad accesso
casualeTramite l’aritmetica degli iteratori è possibile inizializzare
un iteratore direttamente in una posizione desideratavector<T>::iterator i = v.begin() + v.size() / 2;
Intervallo di iteratori Un intervallo è definito tramite due
iteratori Il primo si riferisce al primo elemento
dell’intervallo Il secondo si riferisce ad un elemento dopo
l’ultimo Intervalli inclusivi a sinistra, in matematica li
indicheremmo come [inizio, fine) Deve essere possibile raggiungere la fine
dell’intervallo incrementando successivamente il riferimento alla’inizio dell’intervallo
Se l’inizio e la fine dell’intervallo coincidono, l’intervallo è vuoto
Per iterare sull’intervallo compreso tra first e last, è lecito un ciclo del tipowhile (first != last) { // fai qualcosa con *first first++;}
Iterare sugli elementi di un vettore Usando gli indici
for (vector<T>::size_type i = 0; i < v.size(); i++) // fai qualcosa con v[i]
Usando un iteratore for (vector<T>::iterator i =
v.begin(); i != v.end(); i++) // fai qualcosa con *i
Quando si modifica la dimensione di un vettore (es. con push_back), gli iteratori esistenti non sono più validi.
Iteratore costante Tutti i contenitori della libreria standard definiscono
un iteratore costante vector<T>::const_iterator i;
L’iteratore costante va usato quando si usano, ma non si modificano, gli elementi del vettore for (vector<T>::const_iterator i = v.begin(); i !=
v.end(); i++) cout << *i;
Usare *i come se fosse un Lvalue causa un errore di compilazione
Il valore dell’iteratore può essere cambiato, ma non può essere usato l’iteratore per cambiare il valore riferito
const vector<T>::iterator i = v.begin(); L’iteratore non può essere cambiato, ma l’elemento riferito sì! … è decisamente inutile
Un vector costante può avere solo iteratori costanti const vector<T> v;
conts vector<T>::iterator i = v.begin(); // errore!!!vector<T>::const_iterator j = v.end(); // Ok
typedef in vector size_type
iterator const_iterator
reverse_iterator
const_reverse_iterator
difference_type
value_type reference const_reference
tipo numerico senza segno in grado di contenere la dimensione del più grande contenitore possibile
iteratore iteratore che non consente
di modificare gli elementi iteratore che scorre gli
elementi in ordine inverso iteratore che scorre gli
elementi in ordine inverso e non consente di modificare gli elementi
tipo numerico con segno in grado di contenere la differenza tra due iteratori
il tipo dell’elemento value_type& const value_type&
iteratori in vector v.begin()
v.end()
v.rbegin()
r.rend()
iteratore riferito al primo elemento
iteratore riferito ad un elemento dopo l’ultimo
iteratore al rovescio riferito all’ultimo elemento
iteratore al rovescio riferito ad un elemento prima del primo
di ognuno c’è la versione const e la versione non const
Altri contenitori sequenziali Deque
Coda bidirezionale: permette l’inserimento di elementi in testa o in coda in tempo costante
Inserire o rimuovere elementi nel mezzo comporta spostare elementi a destra o a sinistra
Permetto l’accesso indicizzato agli elementi List
Lista bidirezionale: ogni elemento conosce il precedente ed il successivo
Per accedere ad un elemento occorre attraversare tutti gli elementi precedenti
L’inserimento o la rimozione di un elemento in un punto qualsiasi richiede un tempo costante
Aggiungere elementi ad un contenitore sequenziale c.push_back(t)
inserimento in coda c.push_front(t)
inserimento in testa (solo list e deque) per i vector si può usare
v.insert(v.begin(), t)ma si tratta di un’operazione dispendiosa
c.insert(i, t) inserimento prima dell’iteratore i
c.insert(i, n, t) inserisce n elementi prima dell’iteratore i
c.insert(i, b, e) inseresce prima dell’iteratore i gli
elementi della sequenza compresa tra b ed e
Aggiungere elementi ad un contenitore sequenziale Aggiungere un elemento ad un
contenitore significa copiarlo Non c’è nessuna relazione tra t e
l’elemento nel contenitore dopo che è stato inserito
Gli inserimenti in un vector o deque invalidano gli iteratori Sicuramente quelli che si
riferiscono ad elementi dopo quello inserito
end() è sempre invalidato
Dimensione di un contenitore c.size()
restituisce il numero di elementi c.max_size()
restituisce un valore di tipo c::size_type che rappresenta il numero massimo di elementi che il contenitore può contenere
c.empty() restituisce un bool che indica se c.size() è 0 o no
c.resize(n) imposta la dimensione del contenitore a n elementi se n < c.size() gli elementi in eccesso vengono scartati se si devono aggiungere nuovi elementi, prendono il
valore di default invalida gli iteratori
c.resize(n, t) imposta la dimensione del contenitore ad n elementi se si devono aggiungere nuovi elementi, assumono
valore t
Capienza di un contenitore Un vettore cresce dinamicamente per
far spazio ai nuovi elementi inseriti Viene allocato più spazio di quello
utilizzato dagli elementi attuali, per v.capacity()
restituisce il numero di elementi che possono essere inseriti senza dover allocare della memoria
v.reserve(n) impone che la capacità sia almeno pari ad n può esseere utile ad es. v.reserve(50) prima di
fare un ciclo che chiama 50 volte push_back()
Accesso agli elementi c.front()
se c non è vuoto, è il primo elemento corrisponde a *c.end()
c.back() se c non è vuoto, è l’ultimo elemento
corrisponde a *--c.end() c[n]
l’elemento di indice n solo per vector e deque
c.at(n) l’elemento di indice n solleva l’eccezione out_of_range se
l’indice non è valido solo per vector e deque
Rimozione di elementi c.erase(i)
rimuove l’elemento a cui fa riferimento l’iteratore i
c.erase(b, e) rimuove la sequenza di elementi
compresa tra gli iteratori b ed e c.clear()
rimuove tutti gli elementi pop_back
Rimuove l’ultimo elemento pop_front
Rimuove il primo elemento Solo per list e deque (non per vector)
Operazioni su list l1.sort()
ordinamento crescente l1.splice(i, l2)
sposta i valori della list l2 a partire dall’iteratore i di questa list (senza copiarli)
l1.splice(i, l2, e) muove l’elemento *e dalla lista l2 prima di i in
questa lis (senza copiarlo) l1.merge(l2)
fusione di liste ordinate l1.unique()
rimuove gli elementi duplicati l1.remove(v)
rimuove tutte le istanze di v l1.reverse()
inverte l’ordine della list
deque
tipi e operatori di vector… … più operazioni su testa
front() push_front() pop_front()
una coda a due capi, ottimizzata per operazioni che si svolgono alle estremità
Adattatori per sequenze : stacktemplate< typename T, typename C = deque<T> >class stack {protected: C c;public: typedef typename C::value_type value_type; typedef typename C::reference reference; typedef typename C::const_reference const_reference; typedef typename C::size_type size_type; typedef C container_type; explicit stack(const C& __c = C()) : c(__c) {} bool empty() const { return c.empty(); } size_type size() const { return c.size(); } reference top() { return c.back(); } const_reference top() const { return c.back(); } void push(const value_type& __x) { c.push_back(__x); } void pop() { c.pop_back(); }};
Adattatori per sequenze: queuetemplate<typename T, typename C = deque<T> >class queue {protected: C c;public: typedef typename C::value_type value_type; typedef typename C::reference reference; typedef typename C::const_reference const_reference; typedef typename C::size_type size_type; typedef C container_type; explicit queue(const C& __c = C()) : c(__c) {} bool empty() const { return c.empty(); } size_type size() const { return c.size(); } reference front() { return c.front(); } const_reference front() const { return c.front(); } reference back() { return c.back(); } const_reference back() const { return c.back(); } void push(const value_type& __x) { c.push_back(__x); } void pop() { c.pop_front(); }};
Adattatori per sequenze: priority_queuetemplate<typename T, typename C = vector<T>, typename Cmp = less<typename C::value_type> > class priority_queue {protected: C c; Cmp comp;public: typedef typename C::value_type value_type; typedef typename C::reference reference; typedef typename C::const_reference const_reference; typedef typename C::size_type size_type; typedef C container_type; explicit priority_queue(const Cmp& __x = Cmp(), const C& __s = C()) : c(__s),
comp(__x) { std::make_heap(c.begin(), c.end(), comp); } template<typename In> priority_queue(In __first, In __last, const Cmp& __x =
Cmp(), const C& __s = C()) : c(__s), comp(__x) { c.insert(c.end(), __first, __last); std::make_heap(c.begin(), c.end(), comp); }
bool empty() const { return c.empty(); } size_type size() const { return c.size(); } const_reference top() const { return c.front(); } void push(const value_type& __x); void pop();};
Contenitori associativi set<T>
insieme multiset<T>
insieme con possibilità di valori ripetuti map<K, T>
mappa associativa Ad un oggetto di tipo T è associata una chiave
di tipo K Data la chiave è possibile accedere
direttamente all’elemento
multimap<K, T> mappa associativa con possibilità di
chiavi ripetute
pair tipo generico che identifica una coppia
definito come template<class First, class Second> class pair { //… }
es. pair<string, int> voce(“Mauro”, 12); make_pair(f, s) restituisce un pair,
inferendo i parametri di tipo dai tipo di f e s p1.first è il membro pubblico
corrispondente al primo elemento p1.second è il membro pubblico
corrispondente al secondo elemento p1 < p2 restituisce (p1.first < p2.first) || (!
(p2.first < p1.first) && p1.second < p2.second)
p1==p2 resituisce (p1.first == p2.first) && (p1.first == p2.first)
Contenitori associativi: tipi membro Oltre ai tipi definiti anche dai
contenitori vettoriali, i contenitori associativi definiscono key_type
Il tipo della chiave mapped_type
Il tipo del valore contenuto value_type
presente anche per i contenitori sequenziali, nei contenitori associativi corrisponde a pair<const key_type, mapped_type>
key_compare Il tipo del criterio di confronto
Contenitori associativi: operazioni m[k]
Restituisce l’elemento il cui valore della chiave è k Solo per contenitori con chiave unica
m.find(k) Cerca l’elemento con chiave k
m.lower_bound(k) cerca il primo elemento con chiave k
m.upper_bound(k) cerca il primo elemento con chiave maggiore di k
m.equal_range(k) restituisce un pair il cui primo elemento è
m.lower_bound(k) ed il secondo è m.upper_bound(k)
Contenitori associativi: iteratori Dereferenziando un iteratore di
un contenitore associativo si ottiene un pair esempio:
map<string, int> m;// …map<string, int>::iterator i = m.begin();cout << i->first; // stampa la chiavecout << i->second; // stampa il valore
Aggiungere elementi Si può usare l’operatore [] per aggiungere
elementi usare un valore della chiava che non è già
presente ha l’effetto di aggiungere un elemento esempio
map<string, int> frequenza;frequenza[“casa”] = 2; // aggiunge un elemento la cui chiave è “casa” e imposta il valore a 2
in alternativa, si può usare il membro insert(), es.frequenza.insert(make_pair(“casa”, 2)); non ha effetto se esiste già un elemento la cui
chiave è “casa”
Provare il semplice esercizio: memorizzare e stampare la frequenza delle parole lette fa un flusso di input (suggerimento: usare frequenza[parola]++).
map Contenitore associativo, permette di
recuperare un elemento sulla base del suo valore
map<K, T>, dove K è il tipo della chiave e T è il tipo dell’elemento Es.
map<string, int> rubrica;string s;// …int i = rubrica[s];
Non possono essere presenti due elementi con la stessa chiave
L’accesso ad un elemento richiede un tempo logaritmico rispetto alla numerosità degli elementi
multimap
una multimap è una map che può avere due o più elementi con la stessa chiave permette di recuperare tutti gli
elementi con un certo valore della chiave
accesso in tempo logaritmico
set
insieme ordinato di elementi tutti gli elementi debbono essere
diversi tra loro accesso in tempo logaritmico ad
un qualsiasi elemento dell’insieme
Recommended