1 / 42

Elementi di programmazione ad oggetti a. a. 2009/2010

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.

Download Presentation

Elementi di programmazione ad oggetti a. a. 2009/2010

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Elementi di programmazione ad oggettia. a. 2009/2010 Corso di Laurea Magistrale in Ingegneria Elettronica Docente: Mauro Mazzieri, Dipartimento di Ingegneria Informatica, Gestionale e dell’Automazione

  2. Lezione 10.1 La libreria standard: i contenitori.

  3. 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

  4. 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>

  5. 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à

  6. Contenitori sequenziali • Vector • list size begin end begin

  7. 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;

  8. 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

  9. 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!

  10. 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

  11. 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

  12. 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

  13. 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);

  14. 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 casuale Tramite l’aritmetica degli iteratori è possibile inizializzare un iteratore direttamente in una posizione desiderata vector<T>::iterator i = v.begin() + v.size() / 2;

  15. 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++;}

  16. 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.

  17. 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

  18. 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& typedef in vector

  19. 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 iteratori in vector di ognuno c’è la versione const e la versione non const

  20. 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

  21. 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ò usarev.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

  22. 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

  23. 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

  24. 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()

  25. 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

  26. 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)

  27. 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

  28. 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à

  29. Adattatori per sequenze : stack template< 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(); } };

  30. Adattatori per sequenze: queue template<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(); } };

  31. Adattatori per sequenze: priority_queue template<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(); };

  32. 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

  33. map map

  34. 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)

  35. 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

  36. 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)

  37. 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

  38. 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 • esempiomap<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]++).

  39. 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

  40. 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

  41. set • insieme ordinato di elementi • tutti gli elementi debbono essere diversi tra loro • accesso in tempo logaritmico ad un qualsiasi elemento dell’insieme

  42. multiset • set che consentono la presenza di più elementi uguali • accesso in tempo logaritmico a qualsiasi elemento

More Related