Risposte:
const_iterator
s 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 const
cambiano il valore puntato).
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.
const iterater
e const_iterator
.
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?
vector
e 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.
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 bar
dovrebbe 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.
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()
è const
sovraccarico e restituisce iterator
o const_iterator
dipende dalla costanza della variabile contenitore:
Un caso comune in cui viene visualizzato const_iterator
pop-up è quando this
viene utilizzato all'interno di un const
metodo:
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) {}
};
const
rende this
const, 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 int
vs 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.
(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.
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.