Il 0% ha trovato utile questo documento (0 voti)
21 visualizzazioni5 pagine

Corso Cplusplus Lezione 760

Il documento tratta del concetto di iteratori in C++. Gli iteratori consentono di navigare all'interno di strutture dati dinamiche senza conoscerne i dettagli implementativi, ad esempio per incrementare o stampare gli elementi. Vengono illustrati esempi di utilizzo di iteratori con liste ed esempi di codice.

Caricato da

v9ygnxyhk8
Copyright
© © All Rights Reserved
Per noi i diritti sui contenuti sono una cosa seria. Se sospetti che questo contenuto sia tuo, rivendicalo qui.
Formati disponibili
Scarica in formato PDF, TXT o leggi online su Scribd
Il 0% ha trovato utile questo documento (0 voti)
21 visualizzazioni5 pagine

Corso Cplusplus Lezione 760

Il documento tratta del concetto di iteratori in C++. Gli iteratori consentono di navigare all'interno di strutture dati dinamiche senza conoscerne i dettagli implementativi, ad esempio per incrementare o stampare gli elementi. Vengono illustrati esempi di utilizzo di iteratori con liste ed esempi di codice.

Caricato da

v9ygnxyhk8
Copyright
© © All Rights Reserved
Per noi i diritti sui contenuti sono una cosa seria. Se sospetti che questo contenuto sia tuo, rivendicalo qui.
Formati disponibili
Scarica in formato PDF, TXT o leggi online su Scribd
Sei sulla pagina 1/ 5

Questo PDF è stato generato su richiesta di Lamonaca Gabriele Lamonaca

il 23/02/2023 alle 12:00. Attenzione: l'uso di questo file in violazione delle


norme in materia di diritto d'autore costituisce reato.

Lezione del corso C++ - Sviluppare software complessi con C++ e Code:Blocks

Iteratori
Oggetto di questa lezione sono gli iteratori. Si tratta di un elemento rilevante del linguaggio C++, la cui utilità
è strettamente legata al concetto di struttura dati dinamica, affrontato nel corso della scorsa lezione. Prima
di tutto, definiamo il concetto di iteratore.

Cos'è un iteratore
Un iteratore è uno strumento che consente di navigare all'interno di una struttura dati dinamica (ovvero, ad
esempio, di "scorrerne" gli elementi uno a uno), senza preoccuparsi dei dettagli implementativi relativi a tale
struttura.

Immaginiamo, ad esempio, che la struttura dati trattata sia una coda (v. scorsa lezione, ci occuperemo, nel
seguito della lezione, di un esercizio proprio su questa struttura). Una volta dichiarato e associato alla coda,
l' iteratore si comporterà di fatto come un puntatore a un elemento della coda stessa e potrà essere gestito
unicamente tramite i propri metodi, senza cioè fare più riferimento alla struttura dati.

Tra le funzionalità più importanti messe a disposizione dell'iteratore, di cui ci serviremo per la risoluzione
dell'esercizio associato a questa lezione:

• il metodo begin: porta l'iteratore a puntare al nodo iniziale della struttura dati considerata (nel nostro
caso, si tratterà del primo elemento della coda)
• il metodo end: porta l'iteratore a puntare al nodo terminale della struttura dati considerata (nel nostro
caso, si tratterà dell'ultimo elemento della coda)
• operatore di incremento ++: tramite l'utilizzo di tale operatore, l'iteratore viene portato a puntare al
nodo successivo rispetto a quello corrente. Tale operatore consente cioè di "scorrere" con semplicità
la struttura dati, indipendentemente dalle sue caratteristiche specifiche, consentendo all'iteratore di
comportarsi di fatto in modo equivalente a un indice che identifica la cella di un array.

Il paragone tra strutture dati combinate con iteratori e array viene in seguito approfondito: prima a livello
concettuale, nel corso della prossima sezione; poi a livello pratico e implementativo, nelle sezioni
conclusive.

A cosa serve un iteratore


Abbiamo rivisto, proprio nella scorsa lezione, i vantaggi delle strutture dati dinamiche (come coda e stack)
rispetto a una struttura statica, come l'array.
In sintesi, le strutture dinamiche non necessitano di una dimensione fissata, ma possono essere liberamente
espanse e contratte con operazioni di inserimento ed estrazione.

Nel caso di un array invece, è necessario definire una dimensione: al più, si può ottenere di definirla non
durante la stesura, ma durante l'esecuzione del programma, tramite l'uso dei puntatori (v. lezione 7).

Chiediamoci però ora: qual è invece il vantaggio degli array rispetto alle strutture dati dinamiche? La
risposta a questa domanda, come stiamo per osservare, ha molto a che fare con gli iteratori.

Il punto è che l'indicizzazione degli elementi è naturale in un array: a ogni cella cioè è associato, oltre al
contenuto, un numero intero, che ne identifica univocamente la posizione all'interno della struttura. Risulta
molto semplice quindi, ad esempio, "scorrere" l'array aggiornando, a ogni iterazione, l'indice osservato. Tale
procedura però non può essere replicata nel caso di una coda, che necessita, ai fini dell'indicizzazione, di un
passaggio supplementare. Gli iteratori rappresentano proprio tale passaggio.

Per toccare con mano tale aspetto, prendiamo in esame un esempio concreto di programma da realizzare:
prima per mezzo di un array, poi per mezzo di una coda.

Efficacia degli iteratori: un paragone tra array e code


Si tratta di questo: all'utente viene richiesto di inserire una serie di valori numerici (supponiamo numeri interi,
ovvero variabili di tipo int). Tali valori sono memorizzati all'interno di una struttura dati.

Dopo la memorizzazione, i valori devono essere tutti incrementati di 1. Successivamente, stampiamo a


video tutti i valori memorizzati, mantenendo però inalterata la struttura dati (ovvero, ad esempio, senza
estrarne nessuno).

Per prima cosa risolviamo il problema con un array: faremo riferimento, nella prossima sezione, al codice
contenuto in esercizio15a.cpp.

Soluzione per mezzo di un array


Per risolvere con un array il problema illustrato abbiamo bisogno di poter formulare un'ipotesi
supplementare: conoscere a priori il numero massimo di elementi che l'utente può voler inserire oppure
assumere che, nel momento in cui l'utente inizia a inserire i numeri, sappia quanti numeri inserirà (o
perlomeno sappia che non ne inserirà più di una quantità fissata, ad esempio non più di 100 numeri).

Nel caso il numero massimo di elementi da inserire sia noto a priori, potremo procedere a un'allocazione
statica di memoria per l'array; in caso contrario, se le informazioni necessarie si rendono disponibili soltanto
durante l'esecuzione del programma, allocheremo l'array servendoci di un puntatore (v. lezione 7).

Per semplicità, considerando che questa lezione non è incentrata sull'uso degli array, assumiamo che il
numero massimo di elementi che l'utente può voler inserire sia noto a priori e sia uguale a 100. Associamo,
all'interno del nostro programma, 100 a MAX_DIM, per mezzo della keyword define e utilizziamo MAX_DIM
per definire la dimensione dell'array (a cui assegniamo il nome di data):

#define MAX_DIM 100


...
double data [MAX_DIM];

Inseriamo poi gli elementi nell'array, per mezzo di un ciclo do-while:

int choice = -1;


int dim = 0;
...
do
{
cout<<"Inserisci un numero\n";
cin>>data[dim];
dim++;
cout<<"Se desideri uscire dal programma digita 0; in caso contrario digita qualsiasi altro numero\n";
cin>>choice;
}while(choice!=0);

Osserviamo come, tramite la variabile di tipo int e di nome dim, teniamo traccia del numero di elementi
effettivamente inserito nell'array. La variabile dim è infatti incrementata dopo ogni inserimento tramite dim++.
L'ipotesi circa la dimensione massima dell'array, che porta alla definizione di MAX_DIM è utile soltanto per
garantire che in nessun caso sia richiesto di accedere a una cella dell'array di indice superiore alla
dimensione, ovvero a una cella inesistente. Formulata questa ipotesi invece, il rischio che si corre è soltanto
quello che alcune celle dell'array restino inutilizzate: al più uno spreco di memoria dunque, ma non un errore
in esecuzione.

Osserviamo a questo punto come le operazioni di incremento di 1 di ogni valore contenuto in data e di
stampa a video dei suddetti valori (senza alterare il contenuto dell'array) possano essere svolte con grande
semplicità, per mezzo di due cicli for:

//Incremento il valore di tutti gli elementi dell'array di 1


for(i=0;i<dim;i++)
{
data[i]++;
}
//Stampa di tutti gli elementi dell'array
for(i=0;i<dim;i++)
{
cout<<"Valore dell'elemento "<<i+1<<": "<<data[i]<<"\n";
}

Ciò è dovuto al fatto che, come accennato nelle scorse sezioni, l'array è, per sua natura, indicizzato. In altre
parole, per accedere, ad esempio, al primo elemento dell'array data è sufficiente scrivere data[0]; data[1] per
il secondo e così via. Vediamo dunque come "scorrere" l'array da cima a fondo sia naturalmente associato
al progressivo incremento di una variabile e dunque possa essere realizzato con grande naturalezza per
mezzo di un ciclo for.

Verifichiamo ora come ottenere lo stesso risultato combinando una coda e un iteratore.

Soluzione per mezzo di una coda: uso degli iteratori


La prima parte della soluzione per mezzo di una coda (contenuta in esercizio15b.cpp), cioè quella di
inserimento dei dati, non offre particolari spunti di riflessione; ci limitiamo a osservare che, come nel corso
della scorsa lezione, ci serviamo della classe list e, per l'inserimento, del metodo push_back:

#include <list>
...
list<int> data;
int var;
int choice = -1;
//Inserisco gli elementi nell'array
do
{
cout<<"Inserisci un numero\n";
cin>>var;
data.push_back(var);
cout<<"Se desideri uscire dal programma digita 0; in caso contrario digita qualsiasi altro numero\n";
cin>>choice;
}while(choice!=0);

Dato che ci serviamo di una struttura dati dinamica, non è stato necessario formulare alcuna ipotesi circa la
quantità di dati da inserire (il numero di numeri interi che l'utente desidera digitare) se non, e si tratta di
un'ipotesi ineliminabile ma decisamente non limitante, che tale quantità non sia tale da saturare la memoria
del PC su cui viene eseguito il programma.

A questo punto ci proponiamo, sostanzialmente, di replicare i cicli for esaminati nella sezione precedente
(quella relativa agli array) per le operazioni di incremento e di stampa a video. A differenza dell'array però, la
coda non è indicizzata. Dichiariamo perciò un iteratore, che ci consentirà proprio di "scorrere" la coda con
un ciclo for, con la stessa semplicità con cui lo abbiamo fatto per un array.

Assegniamo all'iteratore il nome di iter e specifichiamo, fin dalla dichiarazione, che dovrà essere applicato a
un oggetto di tipo list<int>:

list<int>::iterator iter;

Ora possiamo usare iter per scorrere la coda, prima incrementando di 1 il valore di ciascun elemento in essa
contenuto e poi stampandoli tutti a video. Tramite il metodo begin la variabile iter può essere portata a
puntare all'inizio della coda; tramite il metodo end, alla fine della coda. Incrementare iter porta
automaticamente a puntare all'elemento successivo della coda rispetto a quello corrente. Questi elementi ci
consentono di realizzare i due cicli for necessari alla risoluzione dell'esercizio:

//Incremento il valore di tutti gli elementi della coda di 1


for (iter = data.begin(); iter != data.end(); iter++)
{
*iter = *iter+1;
}
//Stampa di tutti gli elementi della coda
int counter = 1;
for (iter = data.begin(); iter != data.end(); iter++)
{
cout<<"Valore dell'elemento "<<counter<<": "<<*iter<<"\n";
counter++;
}

Scorrere l'intera coda corrisponde a: for (iter = data.begin(); iter != data.end(); iter++). Ovvero: "inizializzo
l'iteratore iter all'inizio (begin) della coda di nome data e lo incremento di una posizione, lungo la coda, a
ogni iterazione, fino a raggiungerne la fine (end)".

Osserviamo inoltre che nell'esercizio appare una variabile supplementare, di nome counter e di tipo int. Tale
variabile non è strettamente funzionale alla risoluzione dell'esercizio stesso, ma rappresenta semplicemente
il modo più immediato per ottenere, nella stampa a video, anche la posizione di ciascun elemento stampato
all'interno della coda, facilitando così la comprensione dell'esempio proposto.

A conclusione del confronto tra le due soluzioni, riassumiamo: la soluzione per mezzo di un array ci
consente di risolvere il problema con una sintassi molto semplice, a prezzo però di fissare il numero
massimo di dati che l'utente può voler inserire.

L'utilizzo di una coda ci permette di non fissare tale numero, risultando quindi più generale. Per poterci
servire di tale soluzione tuttavia, è opportuno introdurre anche il concetto di iteratore. Mentre l'array può
essere gestito direttamente e con immediatezza infatti, una struttura dati dinamica (in questo caso, la coda)
trae beneficio dall'essere affiancata da un iteratore, ottenendo così una semplicità di gestione paragonabile
a quella di un array.

© MRW Corsi - www.mrwcorsi.it - IKIweb Internet Media SRL

Potrebbero piacerti anche