Vettori dinamici Definiremo la classe vector La classe

  • Slides: 21
Download presentation
Vettori dinamici Definiremo la classe vector. • La classe rappresenta un vettore dimensionabile dinamicamente.

Vettori dinamici Definiremo la classe vector. • La classe rappresenta un vettore dimensionabile dinamicamente. • Ha un parametro template che definisce il tipo di dato contenuto nel vettore. • L’implementazione proposta e’ volutamente rozza. • Vedremo come la stessa classe sia implementanta nella Standard Template Library. 12/16/2021 Claudio Rocchini IGMI 1

Vector: implementazione La classe vector e’ definita da tre valori: • Size: la dimensione

Vector: implementazione La classe vector e’ definita da tre valori: • Size: la dimensione corrente del vettore, ovvero il numero dei suoi elementi. • Memo: il numero di elementi allocati realmente, che deve essere maggiore o uguale a size. • Data: la memoria allocata vera e propria. La classe e’ parametrizzata secondo il tipo T, che rappresenta il tipo degli elementi contenuti nel vettore. 12/16/2021 Claudio Rocchini IGMI 2

Vector: Intestazione template<class T> class vector { private: T * data; int size; int

Vector: Intestazione template<class T> class vector { private: T * data; int size; int memo; }; // Parametro template // // Dati privati Memoria allocata Dimensione corrente Dim. memoria allocata Data contiene il vettore vero e proprio, size memorizza la dimensione reale del vettore, memo la memoria allocata (>= size). 12/16/2021 Claudio Rocchini IGMI 3

Vector: costruttore 1 inline { data size memo } vector() = 0; // Inizializzazione

Vector: costruttore 1 inline { data size memo } vector() = 0; // Inizializzazione variabili Il costruttore di default definisce un vettore con zero elementi e zero posizioni allocate. 12/16/2021 Claudio Rocchini IGMI 4

Vector: costruttore 2 Inline vector( int s ) { assert(s>0); data = new T[s];

Vector: costruttore 2 Inline vector( int s ) { assert(s>0); data = new T[s]; assert(data!=0); size = s; memo = s; } // Allocazione memoria // Definizione di size // Definizione di memo Costruttore con allocazione iniziale della dimensione; lo spazio allocato coincide con la dimensione attuale. 12/16/2021 Claudio Rocchini IGMI 5

Vector: distruttore inline void clear() { if(data) delete[] data; // Disallocazione data = 0;

Vector: distruttore inline void clear() { if(data) delete[] data; // Disallocazione data = 0; size = memo = 0; } inline ~vector() { clear(); } E’ importante dichiarare il distruttore della classe; il distruttore si occupa di disallocare la memoria dei dati. 12/16/2021 Claudio Rocchini IGMI 6

Vector: accesso agli elementi inline T & operator[] ( const int i ) {

Vector: accesso agli elementi inline T & operator[] ( const int i ) { assert(i>=0 && i<size); return data[i]; } inline const T & operator[] ( int i ) const { assert(i>=0 && i<size); return data[i]; } Operatore[]: accesso agli elementi del vettore, come al solito di definisce anche la versione costante. 12/16/2021 Claudio Rocchini IGMI 7

Vector: espansione inline void reserve( int m ) { if(m>memo) { T * tmp

Vector: espansione inline void reserve( int m ) { if(m>memo) { T * tmp = new T[m]; // Alloc. nuovo vettore for(int i=0; i<size; ++i) // Copia dati tmp[i] = data[i]; if(data) delete[] data; // Disalloc. memoria data = tmp; memo = s; } } Questo metodo ridimensiona la memoria allocata senza modificare il vettore. 12/16/2021 Claudio Rocchini IGMI 8

Vector: ridimensionamento inline void resize( int s ) { reserve(s); size = s; //

Vector: ridimensionamento inline void resize( int s ) { reserve(s); size = s; // Assegnamento dimensione } Questo metodo espande (o riduce) il numero di elementi del vettore, la chiamata a reserve assicura che la memoria allocata sia sufficiente. 12/16/2021 Claudio Rocchini IGMI 9

Vector: aggiunta di 1 elemento inline void push_back( const T & e ) {

Vector: aggiunta di 1 elemento inline void push_back( const T & e ) { reserve(size+1); data[size++] = e; } Il metodo aggiunge un elemento in coda al vettore; tuttavia, in caso di aggiunta di molti elementi, l’implementazione risulta eccessivamente inefficiente: si esegua una riallocazione (con copiatura dei dati) per ogni elemento aggiunto. 12/16/2021 Claudio Rocchini IGMI 10

Vector: aggiunta efficiente inline void push_back( const T & e ) { if(size==memo) //

Vector: aggiunta efficiente inline void push_back( const T & e ) { if(size==memo) // Necessaria riallocazione { if(memo==0) reserve(256); else reserve(memo*2); } data[size++] = e; } Con questa implementazione, per ogni n inserzioni nel vettore, si eseguono al piu’ log(n) espansioni (perche? ). 12/16/2021 Claudio Rocchini IGMI 11

Vector: controlli inline bool is_empty() const { return size==0; } inline int size() const

Vector: controlli inline bool is_empty() const { return size==0; } inline int size() const { return size; } Il metodo is_empty constrolla se il vettore e’ vuoto. Il metodo size ritorna il numero di elementi del vettore. 12/16/2021 Claudio Rocchini IGMI 12

Vector: assegnamento errato La funzione di assegnamento di default, copia i dati propri della

Vector: assegnamento errato La funzione di assegnamento di default, copia i dati propri della classe nel seguente modo: Inline vector & operator= ( const vector & v ) { data = v. data; size = v. size; memo = v. memo; return *this; } Il funzionamento risulta errato. 12/16/2021 Claudio Rocchini IGMI 13

Vector: assegnamento errato(2) V 1. data V 2. data copia 12/16/2021 Claudio Rocchini IGMI

Vector: assegnamento errato(2) V 1. data V 2. data copia 12/16/2021 Claudio Rocchini IGMI 14

Vector: assegnamento corretto inline vector & operator= ( const vector & v ) {

Vector: assegnamento corretto inline vector & operator= ( const vector & v ) { if(v. size>memo) // Memoria insuff. { if(data) delete[] data; // Riallocazione memo = v. size; data = new T[memo]; } for(int i=0; i<v. size; ++i) // Copia elementi data[i] = v. data[i]; size = v. size; // Aggiorn. dimensione } 12/16/2021 Claudio Rocchini IGMI 15

Vector: esempio di utilizzo … vector<char> v(10); V[3] = ‘x’; v. push_back(‘y’); cout <<

Vector: esempio di utilizzo … vector<char> v(10); V[3] = ‘x’; v. push_back(‘y’); cout << v. size(); vector<char> w; w = v; Vector<char> k[10]; 12/16/2021 // // // vettore di 10 char Assegnamento elemento Aggiunta in coda stampa: 11 Dichiarazione assegnamento vettori // ATTENZIONE: si e’ // dichiarato 10 vettori Claudio Rocchini IGMI 16

Vector: introduzione agli iteratori char v[100]; for(int i=0; i<100; ++i) operazione(v[i]); // (1)Iterazione con

Vector: introduzione agli iteratori char v[100]; for(int i=0; i<100; ++i) operazione(v[i]); // (1)Iterazione con indice for(char* j=v; j<v+100; ++j) // (2)Iteraz. con puntatore operazione(*j); L’iterazione degli elementi di un vettore si effettua di solito tramite indici (caso 1) o tramite puntatori (caso 2). Di solito il secondo metodo e’ piu’ efficiente, non richiede infatti il calcolo dell’indirizzo dell’elemento. Come si procede nel caso della classe vector? 12/16/2021 Claudio Rocchini IGMI 17

Vector: iteratori typedef T * iterator; inline iterator begin() { return data; } inline

Vector: iteratori typedef T * iterator; inline iterator begin() { return data; } inline iterator end() { return data+size; } Definiamo il tipo di dato iterator, che e’ un estensione del puntatore. Definiamo inoltre i metodi begin e end che ritornano gli iteratori all’inizio e alla fine del vettore. 12/16/2021 Claudio Rocchini IGMI 18

Vector: utilizzo degli iteratori vector<char> v(100); vector<char>: : iterator i; for(i=v. begin(); v!=v. end();

Vector: utilizzo degli iteratori vector<char> v(100); vector<char>: : iterator i; for(i=v. begin(); v!=v. end(); ++i) operazione(*i); L’iteratore si utilizza in modo del tutto analogo al puntatore. 12/16/2021 Claudio Rocchini IGMI 19

Vector: un’altra implementazione • Abbiamo visto un’implementazione veramente rozza della classe vector. • In

Vector: un’altra implementazione • Abbiamo visto un’implementazione veramente rozza della classe vector. • In realta’ la classe vector e’ gia’ implementata in maniera piu’ efficiente nella libreria standard del C++. • Tale implementazione ha le stesse funzionalita’ da noi implementate piu’ altre aggiuntive. • La classe vector della libreria standard ha un secondo parametro template, che specifica la politica di riallocazione della memoria. 12/16/2021 Claudio Rocchini IGMI 20

Esercizi Per quanto riguarda la classe vector: • Definire i metodi front() e back()

Esercizi Per quanto riguarda la classe vector: • Definire i metodi front() e back() che ritornano il riferimento al primo e all’ultimo elemento del vettore. • Definire il metodo pop_back() che elimina l’ultimo elemento del vettore. • Definire il metodo erase( iterator p ) che elimina dal vettore l’elemento indicato da p e ricompatta gli altri valori (*) esercizio piu’ impegnativo. 12/16/2021 Claudio Rocchini IGMI 21