Qual è la differenza tra const_iterator e non-const iterator nel C ++ STL?


139

Qual è la differenza tra a const_iteratore an iteratore dove useresti l'uno rispetto all'altro?


1
Bene, il nome const_iterator suona come l'iteratore è const, mentre la cosa a cui questo iteratore indica è la const reale.
talekeDskobeDa

Risposte:


124

const_iterators non ti permettono di cambiare i valori a cui puntano, lo fanno regolarmente iterator.

Come per tutte le cose in C ++, preferisci sempre const, a meno che non ci sia una buona ragione per usare iteratori regolari (cioè vuoi usare il fatto che non constcambiano il valore puntato).


8
In un mondo perfetto sarebbe il caso. Ma con C ++ const è buono solo come la persona che ha scritto il codice :(
JaredPar

mutevole esiste per un'ottima ragione. Viene usato raramente e, guardando la ricerca del codice di Google, sembra esserci un'equa percentuale di usi validi. La parola chiave è uno strumento di ottimizzazione molto potente e non è come rimuoverla migliorerebbe la correttezza const ( coughpointerscoughandcoughreferencescough )
coppro

2
Più come un potente hack. Di tutti i casi che ho mai visto sull'uso della parola chiave "mutable", tutti tranne uno erano un indicatore accurato che il codice era scritto male e che era necessario mutable come hack per aggirare i difetti.
John Dibling,

7
Ha usi legittimi, come la memorizzazione nella cache dei risultati di un lungo calcolo all'interno di una classe const. D'altra parte, è praticamente l'unica volta che ho usato un mutevole in quasi vent'anni di sviluppo del C ++.
Head Geek,

Un esempio generico per l'uso di const_iterator sarebbe quando l'iteratore è un valore
talekeDskobeDa

40

Dovrebbero praticamente essere autoesplicativi. Se l'iteratore punta a un elemento di tipo T, allora const_iterator punta a un elemento di tipo 'const T'.

È sostanzialmente equivalente ai tipi di puntatore:

T* // A non-const iterator to a non-const element. Corresponds to std::vector<T>::iterator
T* const // A const iterator to a non-const element. Corresponds to const std::vector<T>::iterator
const T* // A non-const iterator to a const element. Corresponds to std::vector<T>::const_iterator

Un iteratore const punta sempre sullo stesso elemento, quindi lo stesso iteratore è const. Ma l'elemento a cui punta non deve essere const, quindi l'elemento a cui punta può essere modificato. Un const_iterator è un iteratore che punta a un elemento const, quindi mentre l'iteratore stesso può essere aggiornato (incrementato o decrementato, ad esempio), l'elemento a cui punta non può essere modificato.


1
"Un const iterator punta sempre allo stesso elemento" Questo non è corretto.
John Dibling,

2
Come mai? Nota il carattere di sottolineatura mancante. Sto contrastando una variabile di tipo const std :: vector <T> :: iteratore con std :: vector <T> :: const_iterator. Nel primo caso, lo stesso iteratore è const, quindi non può essere modificato, ma l'elemento a cui fa riferimento può essere modificato liberamente.
jalf

4
Ah, capisco. Sì, ho perso il carattere di sottolineatura mancante.
John Dibling,

3
@JohnDibling Aggiornato per aver spiegato la finezza tra const iteratere const_iterator.
legends2k,

8

Sfortunatamente, molti metodi per i contenitori STL prendono iteratori invece di const_iterators come parametri. Quindi, se hai un const_iterator , non puoi dire "inserisci un elemento prima dell'elemento a cui questo iteratore indica" (dire che una cosa del genere non è concettualmente una violazione const, secondo me). Se vuoi farlo comunque, devi convertirlo in un iteratore non const usando std :: anticipo () o boost :: next () . Per esempio. boost :: next (container.begin (), std :: distance (container.begin (), the_const_iterator_we_want_to_unconst)) . Se il contenitore è un elenco std :: , il tempo di esecuzione per quella chiamata sarà O (n) .

Quindi la regola universale per aggiungere const ovunque sia "logico" farlo, è meno universale quando si tratta di contenitori STL.

Tuttavia, i contenitori boost prendono const_iterators (ad esempio boost :: unordered_map :: erase ()). Quindi, quando usi i contenitori boost, puoi essere "costante aggressivo". A proposito, qualcuno sa se o quando i contenitori STL saranno riparati?


1
Potrebbe essere una questione di opinione. Nel caso di vectore deque, l'inserimento di un elemento invalida tutti gli iteratori esistenti, il che non è molto const. Ma vedo il tuo punto. Tali operazioni sono protette dal contenitore const, non dagli iteratori. E mi chiedo perché non ci sia una funzione di conversione iteratore const-to-nonst nell'interfaccia contenitore standard.
Potatoswatter,

Hai ragione Potatoswatter, io sono categorico, è una questione di opinione per i contenitori ad accesso casuale e container.begin () + (the_const_iterator_we_want_to_unconst - container.begin ()) è O (1) comunque. Mi chiedo anche perché non esiste una funzione di conversione per i contenitori di accesso non casuali, ma forse c'è una buona ragione? Sai se c'è qualche ragione per cui le funzioni per i contenitori di accesso non casuali non accettano const_iterators ?
Magnus Andermo,

"Dire una cosa del genere non è concettualmente una costante violazione, secondo me" - questo è un commento molto interessante, ho i seguenti pensieri sull'argomento. Con semplici puntatori, si può dire int const * foo; int * const foo;e int const * const foo;tutti e tre sono validi e utili, ognuno a modo suo. std::vector<int> const bardovrebbe essere uguale al secondo, ma sfortunatamente viene spesso trattato come il terzo. La causa principale del problema è che non possiamo dire std::vector<int const> bar;quando significa che non c'è modo di ottenere lo stesso effetto int const *foo;di un vettore.
Dgnuff,

5

Usa const_iterator ogni volta che puoi, usa iteratore quando non hai altra scelta.


4

Esempi eseguibili minimi

Gli iteratori non costanti consentono di modificare ciò che indicano:

std::vector<int> v{0};
std::vector<int>::iterator it = v.begin();
*it = 1;
assert(v[0] == 1);

Gli iteratori costanti non:

const std::vector<int> v{0};
std::vector<int>::const_iterator cit = v.begin();
// Compile time error: cannot modify container with const_iterator.
//*cit = 1;

Come mostrato sopra, v.begin()è constsovraccarico e restituisce iteratoro const_iteratordipende dalla costanza della variabile contenitore:

Un caso comune in cui viene visualizzato const_iteratorpop-up è quando thisviene utilizzato all'interno di un constmetodo:

class C {
    public:
        std::vector<int> v;
        void f() const {
            std::vector<int>::const_iterator it = this->v.begin();
        }
        void g(std::vector<int>::const_iterator& it) {}
};

constrende thisconst, il che rendethis->v const.

Di solito puoi dimenticartene con auto , ma se inizi a superare quegli iteratori, dovrai pensarci per le firme del metodo.

Proprio come const e non const, puoi convertire facilmente da non const a const, ma non viceversa:

std::vector<int> v{0};
std::vector<int>::iterator it = v.begin();

// non-const to const.
std::vector<int>::const_iterator cit = it;

// Compile time error: cannot modify container with const_iterator.
//*cit = 1;

// Compile time error: no conversion from const to no-const.
//it = ci1;

Quale usare: analogo a const intvs int: preferisci i costanti ogni volta che puoi usarli (quando non hai bisogno di modificare il contenitore con loro), per documentare meglio la tua intenzione di leggere senza modificare.


0

(come altri hanno già detto) const_iterator non ti consente di modificare gli elementi a cui punta, questo è utile all'interno dei metodi della classe const. Ti consente anche di esprimere il tuo intento.


0

ok Lasciami spiegare con un esempio molto semplice prima senza usare iteratore costante, considera che abbiamo una raccolta di numeri interi casuali "randomData"

    for(vector<int>::iterator i = randomData.begin() ; i != randomData.end() ; ++i)*i = 0;
for(vector<int>::const_iterator i = randomData.begin() ; i!= randomData.end() ; ++i)cout << *i;

Come si può vedere per scrivere / modificare i dati all'interno della raccolta, viene utilizzato un iteratore normale, ma a scopo di lettura è stato utilizzato un iteratore costante. Se provi a utilizzare iteratore costante per primo per il ciclo, otterrai un errore. Come regola empirica utilizzare iteratore costante per leggere i dati all'interno della raccolta.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.