Introduzione agli Array in C++. Argomenti trattati: decadimento a puntatore di un array; conseguenze del meccanismo di decadimento sul passaggio di array a funzioni; array multidimensionali e il concetto di puntatore ad array; come passare array multidimensionali a funzioni; gli iteratori come generalizzazione di un puntatore ad elemento di un array ed una breve scorsa di come usare gli iteratori con gli algoritmi standard (e.g., std::copy) del C++.
4. Che cos’è un array?
¤ Un array si definisce specificandone:
¤ Il tipo dato comune ad ogni cella
¤ Il nome dell’array
¤ La sua dimensione
Un array è un contenitore di oggetti sequenziali
di un singolo tipo dato e di dimensione fissa
int numbers[5]; // an array of five int’s named ‘numbers’
5. ¤ Il compilatore riserva il quantitativo di memoria
necessaria per accomodare 5 elementi di tipo int
¤ Le celle riservate sono contigue
¤ Ogni oggetto nell’array numbers è associato ad un
indice, che permette di accedere all’oggetto
¤ L’indice 0 è associato al primo elemento, l’indice 4 all’ultimo
Rappresentazione in memoria
numbers:
6. Assegnamento ed inizializzazione
¤ Cosa è possibile fare:
¤ Inizializzare un array con una lista di inizializzazione
int numbers[] = {1, 7, 13}; // ok, initialization list
7. Assegnamento ed inizializzazione
¤ Cosa non è possibile fare:
¤ Inizializzare un array come una copia di un’altro array
¤ Assegnare un array
int numbers[] = {1, 7, 13};
int other_numbers[] = numbers // error, copy initialization
int more_numbers[3];
more_numbers[] = {1, 7, 13} // error, assignment
8. Array non inizializzati
¤ Se in un una funzione si definisce un array senza
inizializzarlo, il valore iniziale degli elementi dipende dal
loro tipo T
¤ Se T è un tipo predefinito, gli elementi sono inizializzati a
default (default initialization)
¤ Altrimenti, si usa il costruttore di default di T per inizializzare
tutti gli elementi
¤ Se T non prevede un costruttore di default, il programma
non compila
9. Accedere agli elementi di un array
¤ Per accedere all’i-esimo elemento nell’array si utilizza
l’operatore [] (indexing operator*)
*anche detto subscript operator
int numbers[] = {1, 7, 13};
std::cout << "first element: ” << numbers[0] << std::endl;
10. Accessi fuori dall’intervallo
¤ Accessi fuori dall’intervallo ammissibile di un array sono
considerati undefined behavior
¤ Il programma compila, ma non è possibile prevedere cosa
accadrà dopo aver effettuato l’accesso errato
int numbers[3];
std::cout << numbers[100]; // undefined behavior
11. Accessi fuori dall’intervallo
¤ A differenza di altri linguaggio, il C++ non segnala in fase
d’esecuzione questo tipo di errore
int numbers[3];
std::cout << numbers[100]; // undefined behavior
12. Tipo dato degli elementi di un array
¤ Ogni elemento dell’array è del tipo specificato al
momento della definizione dell’array
¤ È possibile manipolare i singoli elementi di numbers come
una qualsiasi altra variabile di tipo int
int numbers[] = {1, 7, 13};
int x = numbers[2]; // initialize the int variable
// ‘x’ with another int
// variable (numbers[2])
13. Tipo dato di un array
¤ Quando definiamo un array, specifichiamo il tipo dato
dei singoli elementi:
¤ int non denota il tipo dato di numbers, bensì il tipo dato
dei suoi elementi numbers[0]…numbers[4]
int numbers[5]; // an array of five int’s named ‘numbers’
14. Tipo dato di un array
¤ Eppure numbers è una variabile, e come tale deve
avere un tipo dato
int numbers[5]; // an array of five int’s named ‘numbers’
Qual è il tipo dato di un array?
15. Tipo dato di un array
¤ Il tipo array è un tipo dato composto, in quanto dipende
da due fattori distinti:
¤ Il tipo dato T degli elementi
¤ Il numero di elementi m
Per un tipo dato T, T[m] è il tipo dato array di m elementi di tipo T
16. Tipo dato di un array
¤ Esempio: Il tipo dato di numbers è int[5], cioè
“array di 5 elementi di tipo int”
int numbers[5]; // an array of five int’s named ‘numbers’
17. Importanza del tipo composto
¤ È sufficiente che una sola delle due quantità (tipo T,
dimensione m) cambi per cambiare il tipo dato dell’array
¤ numbers e other_numbers non sono variabili dello
stesso tipo:
¤ numbers è di tipo int[5]
¤ other_numbers è di tipo int[10]
int numbers[5]; // numbers is of type int[5]
char letters[5]; // letters is of type char[5]
int other_numbers[10]; // other_numbers is of type int[10]
int other_letters[10]; // other_letters is of type char[10]
18. Dimensioni di un array
¤ La dimensione m è parte del tipo dato dell’array e deve
quindi essere nota a tempo di compilazione (compile-
time)
¤ Le uniche quantità che il compilatore può conoscere
prima che il codice venga eseguito sono le espressioni
costanti
int numbers[5];
5 è un letterale ed è
quindi un’espressione
costante, perchè noto a
compile-time
19. Dimensioni di un array
¤ Un’espressione costante può essere memorizzata in una
variabile costante, usando:
¤ il qualifier const
¤ lo specifier constexpr (dal C++11)
20. Dimensioni di un array
¤ Il tipo dato usato per mantenere la dimensione di un
array è size_t
constexpr size_t DIM = 5; // C++11
size_t const DIM = 5; // C++03
21. Dimensioni di un array
¤ size_t è un tipo intero senza segno in grado di
memorizzare la dimensione del più grande array
allocabile
constexpr size_t DIM = 5; // C++11
size_t const DIM = 5; // C++03
23. Puntatore al primo elemento
¤ Supponiamo di voler memorizzare in un puntatore
l’indirizzo del primo elemento di un array
¤ numbers[0] è un oggetto di tipo int ed ha un indirizzo,
possiamo memorizzare tale indirizzo in un puntatore a
int
int numbers[] = {1, 7, 13, 5, 9};
int* first_element_ptr = &numbers[0];
24. Puntatore al primo elemento
1 7 13 5 9numbers: int[5]
&numbers[0]first_elem_ptr: int*
25. Puntatore al primo elemento
¤ È possibile ottenere il l’indirizzo del primo elemento di un
array utilizzando un’espressione compatta
¤ Dato un array, ad esempio:
¤ Le seguenti due espressioni sono equivalenti:
int numbers[] = {1, 7, 13, 5, 9};
int* first_element_ptr = &numbers[0];
int* first_element_ptr = numbers;
Indirizzo del
primo elemento
Nome
dell’array
26. Decadimento a puntatore
¤ Come può mai funzionare questa cosa?
¤ first_element_ptr è di tipo int*
¤ numbers è di tipo int[5]
int* first_element_ptr = numbers;
27. Decadimento a puntatore
¤ Il tipo int[5] viene implicitamente convertito a int*
¤ Il risultato di tale conversione è un puntatore al primo
elemento dell’array
int* first_element_ptr = numbers;
28. Decadimento a puntatore
¤ Questo fenomeno prende il nome di array-to-pointer
decay
int* first_element_ptr = numbers;
29. Decadimento a puntatore
¤ La conversione non trasforma l’array in un puntatore
¤ Le variabili non cambiano tipo, numbers sarà sempre un
int[5]
int* first_element_ptr = numbers;
30. Decadimento a puntatore
¤ Cosa accade?
¤ Viene creato un puntatore temporaneo di tipo int*,
risultato del decadimento di numbers
¤ Il contenuto di tale puntatore (cioè l’indirizzo di numbers[0])
viene copiato in first_element_ptr
¤ Al termine dell’istruzione, il puntatore temporaneo viene
distrutto
int* first_element_ptr = numbers;
31. Decadimento a puntatore
¤ Nel valutare un’espressione che coinvolge un array di
tipo T[m], il compilatore:
¤ Se l’espressione è corretta, mantiene il tipo T[m]
¤ In caso contrario, converte T[m] a T*
int numbers[] = {1, 7, 13, 5, 9};
size_t numbers_size = sizeof(numbers); // numbers is
// treated as a
// int[5]
int* ptr_to_1st_element = numbers; // numbers is converted
// to int*
32. Perdita di informazione
¤ Il fenomeno si chiama decadimento perchè viene persa
dell’informazione
¤ Una volta convertito in puntatore, non è più possibile
conoscere la dimensione dell’array
¤ Ho solo un puntatore al primo elemento, ma quanti elementi
sono presenti nell’array?
¤ In altre parole, l’unico punto in comune tra i tipi dato
T[m] e T[n] è che entrambi decadono a T*
¤ I rispettivi decadimenti condividono lo stesso tipo dato T*
33. Aritmetica dei puntatori
¤ L’operatore [] permette di accedere e manipolare gli
elementi di un array
¤ Un secondo modo di interagire con gli elementi di un
array è utilizzare l’aritmetica dei puntatori
34. Aritmetica dei puntatori
¤ L’aritmetica dei puntatori si compone di un insieme di
operazione (aritmetiche) sui puntatori, in particolare:
¤ Incremento e decremento
¤ Addizione e sottrazione
¤ Confronto
¤ Assegnamento
35. Aritmetica dei puntatori
¤ Dato un puntatore ptr al primo elemento di un array,
l’espressione ptr + i restituisce un puntatore all’i-esimo
elemento dell’array
int numbers[] = {1, 7, 13, 5, 9};
int* first_element_ptr = numbers;
int* third_element_ptr = first_element_ptr + 2;
int* fifth_element_ptr = first_element_ptr + 4;
std::cout << "the third element is " << *third_element_ptr;
third_element_ptr è un
puntatore, per ottenere il
valore puntatato serve
l’operatore di indirezione *
37. Aritmetica dei puntatori
¤ Grazie al decadimento è possibile evitare l’uso di variabili
d’appoggio (come first_element_ptr):
¤ Le parentesi sono importanti
¤ *numbers + 4 è equivalente a numbers[0] + 4
¤ *(numbers + 4) è equivalente a numbers[4]
int numbers[] = {1, 7, 13, 5, 9};
std::cout << "the third element is " << *(numbers + 2);
std::cout << "the fifth element is " << *(numbers + 4);
38. Indexing operator vs.
aritmetica dei puntatori
¤ L’operatore [] è definito in termini di aritmetica dei
puntatori
¤ L’operatore [] è dunque una scrittura sintetica per
effettuare una somma su puntatore
Dato un array a ed un indice i,
l'operazione a[i] è implementata come *(a + i)
39. Indexing operator vs.
aritmetica dei puntatori
¤ L’operatore [] è in realtà un operatore definito sui
puntatori (si può usare sugli array grazie al decadimento)
int* first_elem_ptr = numbers;
std::cout << first_elem_ptr[3]; // implemented as
// *(first_elem_ptr + 3)
40. Indexing operator vs.
aritmetica dei puntatori
¤ Le due seguenti scritture sono equivalenti (la somma è
commutativa)
std::cout << numbers[2]; // implemented as *(numbers + 2)
std::cout << 2[numbers]; // implemented as *(2 + numbers)
41. Gli array non sono puntatori
¤ RICORDA: Gli array non sono puntatori
¤ Sono due tipi dati distinti, anche se strettamente legati
¤ Per convincersene è sufficiente notare che la loro
rappresentazione in memoria è diversa
…a: T[m]
ptr: T*
0 m-1
43. La funzione sum_int_array
¤ Vogliamo scrivere una funzione sum_int_array che
restituisca la somma dei valori contenuti in un array di
interi
int main() {
int numbers[] = {1, 7, 13, 5, 9};
int sum = sum_int_array(numbers);
std::cout << ”the elements sum up to " << sum
<< std::endl;
}
Viene stampato 35
44. Passare array a funzioni
¤ Non è possibile passare ad una funzione un array per
copia
¤ Non è possibile inizializzare l’array della funzione
chiamata copiando il contenuto dell’array della funzione
chiamante
45. Passaggio per indirizzo
¤ Nel passaggio per indirizzo viene fornita in ingresso alla
funzione chiamata una copia dell’indirizzo del parametro
che si vuole passare
¤ IDEA: fare in modo che sum_int_array riceva in
ingresso:
¤ Un puntatore alla prima celladell’array
¤ La dimensione dell’array
int sum_int_array(int* array, size_t dim_array);
46. Passaggio per indirizzo
¤ La funzione chiamante fornisce l’indirizzo del primo
elemento dell’array e la dimensione dell’array
int main() {
int numbers[] = {1, 7, 13, 5, 9};
int sum = sum_int_array(numbers, 5);
}
Il 1° parametro in ingresso a
sum_int_array è un
puntatore, quindi l’array
numbers decade a int*
int sum_int_array(int* array, size_t dim_array);
47. Passaggio per indirizzo
¤ Grazie al decadimento, è possibile usare il nome
dell’array per ottenere l’indirizzo del primo elemento
int main() {
int numbers[] = {1, 7, 13, 5, 9};
int sum = sum_int_array(numbers, 5);
}
Il 1° parametro in ingresso a
sum_int_array è un
puntatore, quindi l’array
numbers decade a int*
int sum_int_array(int* array, size_t dim_array);
48. Passaggio per indirizzo
¤ L’implementazione di sum_int_array è dunque
¤ L’operatore [] ci permette di manipolare il puntatore array
con la stessa sintassi che useremmo per un vero array
int sum_int_array(int* array, size_t dim_array) {
int sum = 0;
for (size_t i = 0; i < dim_array; ++i)
sum += array[i];
return sum;
}
49. Sintassi alternativa
¤ Esiste una sintassi alternativa per acquisire l’indirizzo del
primo elemento di un array
¤ I tre seguenti prototipi di sum_int_array sono equivalenti:
¤ NOTA: tale equivalenza vale unicamente quando si dichiara
una funzione
int sum_int_array(int* array, size_t dim_array);
int sum_int_array(int array[], size_t dim_array);
int sum_int_array(int array[5], size_t dim_array);
50. Sintassi alternativa
¤ L’utilizzo di uno dei due prototipi alternativi non significa
che l’array verrà passato per copia
¤ la variabile array è di tipo int*, a prescindere da quale
scrittura si utilizzi
¤ I prototipi alternativi danno l’illusione di agire su degli array
¤ Sono una delle cause della confusione tra array e puntatori
int sum_int_array(int array[], size_t dim_array);
int sum_int_array(int array[5], size_t dim_array);
52. Array multidimensionali
¤ Gli array multidimensionali permettono estendere il
concetto di array a più di una dimensione
1 7 14
8 6 12
27 32 5
53. Array multidimensionali
¤ Nei normali array è sufficiente un solo indice per
identificare un elemento dell’array
¤ Negli array multidimensionali sono necessari tanti indici
quante dimensioni
1 7 14
8 6 12
27 32 5
indice di
riga i
indice di
colonna j
55. Array di array
¤ In C++ non esistono array multidimensionali
¤ Gli array multidimensionali vengono realizzati mediante
array di array, cioè array i cui elementi sono a loro volta
array
56. Array di array
¤ Come per ogni array si definisce il numero di elementi
¤ Ogni elemento è però a sua volta un array
¤ Bisogna specificare una seconda quantità: il numero di
elementi in ogni sotto-array
int matrix[4][3]; // matrix is an array of 4 elements;
// each element is a int[3]
57. Array di array
¤ Esempio: matrix è un array di 4 elementi di tipo int[3]
¤ È la dimensione più interna che determina il numero di
sotto-array
int matrix[4][3];
58. Array di array
¤ L’uso di alias per i tipi dato può aiutarci a rendere più
chiaro il concetto
¤ matrix_row è un altro nome per il tipo int[3]
using matrix_row = int[3];
59. Array di array
¤ L’uso di alias per i tipi dato può aiutarci a rendere più
chiaro il concetto
¤ Così che matrix sia definibile come:
matrix_row matrix[4]; // an array of 4 elements of type
// matrix_row (i.e., of int[3])
using matrix_row = int[3];
60. Rappresentazione in memoria
¤ Abbiamo introdotto due rappresentazioni per gli array
multidimensionali
1 7 14
8 6 12
27 32 5
1 7 14
8 6 12
27 32 5
!
61. Rappresentazione in memoria
¤ Tali rappresentazioni sono intuitive, ma non riflettono
come un array multidimensionale è realmente
memorizzato in memoria
1 7 14
8 6 12
27 32 5
1 7 14
8 6 12
27 32 5
!
62. Rappresentazione in memoria
¤ Sappiamo che la memoria è modellata come una
sequenza di celle di memoria
¤ Gli array multidimensionale sono dunque memorizzati
come una sequenza di celle contigue
matrix: 1 7 14 8 16 12 27 32 5
matrix[0] matrix[1] matrix[2]
63. Inizializzazione
¤ Gli array multidimensionali possono essere inizializzati
mediante una lista di inizializzazione
¤ Le parentesi {} demarcano l’inizio e la fine di ogni riga
¤ Una scrittura equivalente (anche se meno leggibile):
int matrix[3][3] = {{1, 7, 14},
{8, 16, 12},
{27, 32, 5}};
int matrix[3][3] = {1, 7, 14, 8, 16, 12, 27, 32, 5};
64. Accedere agli elementi
¤ Per accedere all’(i,j)-esimo elemento si utilizza una
sequenza di operatori []
int matrix[3][3] = {{1, 7, 14},
{8, 16, 12},
{27, 32, 5}};
std::cout << "element (2,2): " << matrix[2][2];
65. Puntatore al primo elemento
¤ Supponiamo di voler memorizzare in un puntatore
l’indirizzo del primo elemento di un array multidim.
int matrix[3][3] = {{1, 7, 14},
{8, 16, 12},
{27, 32, 5}};
66. Puntatore al primo elemento
¤ matrix è un array di righe
¤ matrix[0] è di tipo int[3] (prima riga della matrice)
¤ Per salvarne l’indirizzo, è necessario un puntatore a int[3]
¤ Come si definisce un puntatore a tipo T[m]?
int matrix[3][3] = {{1, 7, 14},
{8, 16, 12},
{27, 32, 5}};
67. Puntatore ad array
¤ La sintassi per dichiarare un puntatore ad array è
leggermente diversa da quella usata fino ad adesso:
¤ Le parentesi sono importanti
int numbers[] = {1, 7, 13, 5, 9};
int (*numbers_ptr)[5] = &numbers; // numbers_ptr points to
// numbers
int* array_of_pointers[5] // array of 5 elements
// of type pointers to int
int (*pointer_to_array)[5] // pointer to an array
// of 5 elements of type int
69. Puntatore al primo elemento
¤ Possiamo ottenere un puntatore al primo elemento di un
array multidimensionale come:
¤ Ovviamente possiamo usare il decadimento:
int matrix[3][3] = {{1, 7, 14},
{8, 16, 12},
{27, 32, 5}};
int (*ptr_to_first_row)[3] = &matrix[0];
int (*ptr_to_first_row)[3] = matrix;
71. Alias per i sotto-array
¤ L’uso di alias per i tipi dato può nuovamente aiutarci a
rendere più leggibile il codice:
int matrix[3][3] = {{1, 7, 14},
{8, 16, 12},
{27, 32, 5}};
using matrix_row = int[3];
matrix_row* ptr_to_first_row = matrix;
72. Aritmetica dei puntatori
¤ L’aritmetica dei puntatori è definita anche su array
multidim.
¤ matrix è un array di righe
¤ Ogni elemento dell’array matrix è una riga della matrice
¤ Spostarsi di un elemento vuol dire passare alla riga successiva
int matrix[3][3] = {{1, 7, 14},
{8, 16, 12},
{27, 32, 5}};
using matrix_row = int[3];
matrix_row* ptr_to_first_row = matrix;
matrix_row* ptr_to_second_row = ptr_to_first_row + 1;
matrix_row* ptr_to_third_row = ptr_to_first_row + 2;
74. Aritmetica dei puntatori
¤ Dato un puntatore ad una riga dell’array multidim.
¤ Si dereferenzia il puntatore per ottenere la riga
¤ Si utilizza l’aritmetica dei puntatori per per ottenere un
particolare elemento della riga
int matrix[3][3] = {{1, 7, 14},
{8, 16, 12},
{27, 32, 5}};
using matrix_row = int[3];
matrix_row* ptr_to_third_row = matrix + 2;
std::cout << "element (2,2): " << *(*ptr_to_third_row + 2);
terza riga dell’array multidim.
terzo elemento della terza riga
75. Aritmetica dei puntatori
¤ Grazie al decadimento, possiamo combinare le due
operazioni aritmetiche per ottenere un elemento
dell’array multidimensionale
int matrix[3][3] = {{1, 7, 14},
{8, 16, 12},
{27, 32, 5}};
using matrix_row = int[3];
matrix_row* ptr_to_third_row = matrix + 3;
std::cout << "element (2,2): " << *(*(matrix + 2) + 2);
terza riga dell’array multidim.
terzo elemento della terza riga
76. Indexing operator vs.
aritmetica dei puntatori
¤ Il comportamento dell’operatore [] rimane invariato
¤ Non è una nuova definizione dell’operatore, le due
operazioni vengono solo eseguite in cascata
¤ Si può pensare ad a[i][j] come (a[i])[j]:
¤ Si estrae la riga i-esima
¤ Da questa si seleziona il j-esimo elemento
Dato un array multdimensionale a e due indici i e j,
l'operazione a[i][j] è implementata come *(*(a + i) + j)
77. Passare array multidimensionali
a funzioni
¤ Supponiamo di voler scrivere una funzione
sum_int_matrix che sommi i valori di un array multidim.
di interi
int main () {
int matrix[3][3] = {{1, 7, 14},
{8, 16, 12},
{27, 32, 5}};
int sum = sum_int_matrix(matrix);
std::cout << "the elements sum up to " << sum
<< std::endl;
}
78. Passaggio per indirizzo
¤ IDEA: fare in modo che sum_int_matrix accetti un
puntatore alla prima riga
¤ Un puntatore alla prima riga permette di:
¤ Muoversi tra gli elementi della stessa riga
¤ Muoversi tra righe successive
int sum_int_matrix(int (*matrix)[3], size_t row_num);
Puntatore ad un array di 3
elementi di tipo int, cioè un
puntatore ad un riga
dell’array multidimensionale
79. Passaggio per indirizzo
¤ Quando si passano array multidim. tutte le dimensioni
tranne la prima devono essere note a compile-time
¤ L’accesso ad un array multidim. di tipo T[m][n] avviene
mediante un puntatore a tipo T[n]
¤ L’uso del puntatore ci permette di non conoscere m, ma il
valore n deve comunque essere noto
int sum_int_matrix(int (*matrix)[3], size_t row_num);
int sum_int_array(int* array, size_t dim_array);
In caso di array
nessuna
dimensione è
nota compile-
time
Il numero di elementi in
ogni riga deve essere
noto a compile-time
80. Passaggio per indirizzo
¤ L’implementazione di sum_int_matrix è dunque:
¤ L’operatore [] ci permette di manipolare il puntatore
matrix con la stessa sintassi che useremmo per un vero
array multidimensionale
int sum_int_matrix(int (*matrix)[3], size_t row_num) {
int sum = 0;
for (size_t i = 0; i < row_num; ++i)
for (size_t j = 0; j < 3; ++j)
sum += matrix[i][j];
return sum;
}
81. Sintassi alternativa
¤ Come per gli array, esiste una sintassi alternativa per
acquisire l’indirizzo della prima riga di un array multidim.
¤ I tre seguenti prototipi di sum_int_matrix sono
equivalenti:
¤ NOTA: tale equivalenza vale unicamente quando si
dichiara una funzione
int sum_int_matrix(int (*matrix)[3], size_t row_num);
int sum_int_matrix(int matrix[][3], size_t row_num);
int sum_int_matrix(int matrix[3][3], size_t row_num);
83. Container
¤ Esempi:
Un array è un contenitore di oggetti sequenziali
di un singolo tipo dato e di dimensione fissa
Un contenitore è un oggetto in grado di memorizzare altri oggetti
(detti elementi)
Un vector è un contenitore di oggetti sequenziali
di un singolo tipo dato e di dimensione variabile
85. Iteratori
¤ Come suggerito dal nome, gli iteratori sono utilizzati per
iterare (scorrere) tra gli elementi di un container
¤ In questo modo:
¤ Il container ha il solo compito di utilizzare una strategia di
memorizzazione per preservare gli elementi in memoria
¤ L’iteratore ha il solo compito di fornire uno strumento di
accesso agli elementi
86. Iteratori
¤ Ogni iteratore è associato ad un elemento del
corrispettivo container
1 7 13 5 9numbers:
it_1st_elem
int[5]
it_3rd_elem
87. Iteratore di inizio e fine
¤ Ogni iteratore è associato ad un elemento del
corrispettivo container
¤ In particolare, per ogni container identifichiamo due
iteratori speciali:
¤ Un iteratore posizionato in corrispondenza del 1° elemento
¤ Un iteratore in posizione successiva all’ultimo elemento
88. Iteratore di inizio e fine
1 7 13 5 9numbers:
begin_iterator
int[5]
end_iterator
89. Iteratore di inizio e fine
¤ Il C++11/14 fornisce due funzioni per ottenere facilmente
entrambi gli iteratori:
auto begin_it = std::begin(numbers);
auto end_it = std::end(numbers);
90. Iteratore
¤ Si può chiedere ad un iteratore:
¤ di recuperare un elemento
¤ di muoversi da un elemento all’altro
91. Recuperare un elemento
¤ Ogni iteratore è associato ad un elemento del
corrispettivo container
¤ Dato un iteratore, è possibile ottenere il valore a lui
associato
auto begin_it = std::begin(numbers);
std::cout << "the first element of numbers is: "
<< *begin_it << std::endl;
Anteponendo l’asterisco
otteniamo il valore associato
all’iteratore begin_it
92. Muoversi da un elemento all’altro
¤ Possiamo usare l’operazione aritmetica di somma per
spostare l’iteratore di una posizione
auto it = std::begin(numbers);
std::cout << "first element: " << *it << std::endl;
++it;
std::cout << "second element: " << *it << std::endl;
Sommando +1 al valore
dell’iteratore mi sposta alla
posizione successiva
93. Muoversi da un elemento all’altro
1 7 13 5 9numbers:
it
int[5]
++it
94. Muoversi da un elemento all’altro
1 7 13 5 9numbers:
it
int[5]
95. Iteratori
¤ Un iteratore è una generalizzazione del concetto di
puntatore
¤ In particolare, come con i puntatori:
¤ Un iteratore è un oggetto che punta ad un altro oggetto
¤ L’elemento puntato è recuperabile mediante l’operatore *
¤ È possibile muoversi puntare all’elemento successivo
mediante operazioni aritmetiche
96. Navigare l’array
¤ Una volta disponibili gli iteratori di inizio e di fine è
possibile navigare l’array
for (auto it = begin_it; it != end_it; ++it)
std::cout << *it << std::endl;
1 7 13 5 9numbers:
begin_it int*
int[5]
end_itit
97. Algoritmi standard
¤ Gli iteratori possono essere utilizzati per compiere una
moltitudine di operazioni sui container
¤ Tutti gli algoritmi standard del C++ accettano in ingresso
una coppia di iteratori
¤ La coppia delimita la porzione del container su cui si vuole
agire
¤ In questo modo ogni algoritmo può essere usato con un
qualsiasi container, purchè esponga degli iteratori
98. Algoritmi standard
¤ Esempio: copiare un container
¤ Esempio: sommare i valori in un container
¤ Il codice non sarebbe cambiato anche se numbers fosse
stato un vector o un set
int other_numbers[5];
std::copy(std::begin(numbers),
std::end(numbers), std::begin(other_numbers));
int sum = std::accumulate(std::begin(numbers),
std::end(numbers), 0);
100. Bibliografia
¤ S. B. Lippman, J. Lajoie, B. E. Moo, C++ Primer (5th Ed.)
¤ B. Stroustrup, The C++ Programming Language (4th Ed.)
¤ The Gang of Four, Design Patterns - Elements of Reusable
Object Oriented Software
¤ HP, Standard Template Library Programmer's Guide
https://www.sgi.com/tech/stl/
¤ Stackoverflow FAQ, “How do I use arrays in C++?”
http://stackoverflow.com/questions/4810664/how-do-i-use-
arrays-in-c