Réponses:
const_iterator
Les s ne vous permettent pas de modifier les valeurs vers lesquelles ils pointent, les iterator
s ordinaires le font.
Comme pour toutes les choses en C ++, préférez toujours const
, sauf s'il y a une bonne raison d'utiliser des itérateurs réguliers (c'est-à-dire que vous voulez utiliser le fait qu'ils ne const
changent pas la valeur pointée).
Ils devraient être assez explicites. Si l'itérateur pointe vers un élément de type T, alors const_iterator pointe vers un élément de type 'const T'.
C'est fondamentalement équivalent aux types de pointeurs:
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 itérateur const pointe toujours vers le même élément, donc l'itérateur lui - même est const. Mais l'élément sur lequel il pointe n'a pas besoin d'être const, donc l'élément vers lequel il pointe peut être modifié. Un const_iterator est un itérateur qui pointe vers un élément const, donc bien que l'itérateur lui-même puisse être mis à jour (incrémenté ou décrémenté, par exemple), l'élément sur lequel il pointe ne peut pas être modifié.
const iterater
et const_iterator
.
Malheureusement, de nombreuses méthodes pour les conteneurs STL prennent des itérateurs au lieu de const_iterators comme paramètres. Donc, si vous avez un const_iterator , vous ne pouvez pas dire "insérer un élément avant l'élément vers lequel cet itérateur pointe" (dire une telle chose n'est pas conceptuellement une violation de const, à mon avis). Si vous voulez quand même le faire, vous devez le convertir en un itérateur non const en utilisant std :: advance () ou boost :: next () . Par exemple. boost :: next (container.begin (), std :: distance (container.begin (), the_const_iterator_we_want_to_unconst)) . Si le conteneur est une liste std :: , le temps d'exécution de cet appel sera O (n) .
Ainsi, la règle universelle pour ajouter const partout où il est "logique" de le faire, est moins universelle en ce qui concerne les conteneurs STL.
Cependant, les conteneurs boost prennent des const_iterators (par exemple, boost :: unordered_map :: erase ()). Ainsi, lorsque vous utilisez des conteneurs boost, vous pouvez être "const agressif". Au fait, est-ce que quelqu'un sait si ou quand les conteneurs STL seront réparés?
vector
et deque
, l'insertion d'un élément invalide tous les itérateurs existants, ce qui n'est pas très const
. Mais je comprends votre point de vue. Ces opérations sont protégées par le caractère du conteneur const
, pas par les itérateurs. Et je me demande pourquoi il n'y a pas de fonction de conversion d'itérateur const-to-nonconst dans l'interface de conteneur standard.
int const * foo;
int * const foo;
et int const * const foo;
tous les trois sont valables et utiles, chacun à sa manière. std::vector<int> const bar
devrait être le même que le second, mais il est malheureusement souvent traité comme le troisième. La cause première du problème est que nous ne pouvons pas dire std::vector<int const> bar;
quand cela signifie qu'il n'y a aucun moyen d'obtenir le même effet que int const *foo;
dans un vecteur.
Exemples exécutables minimaux
Les itérateurs non const vous permettent de modifier ce qu'ils pointent:
std::vector<int> v{0};
std::vector<int>::iterator it = v.begin();
*it = 1;
assert(v[0] == 1);
Les itérateurs const ne:
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;
Comme indiqué ci-dessus, v.begin()
est const
surchargé et retourne soit iterator
ou en const_iterator
fonction de la const-ness de la variable de conteneur:
Un cas courant où const_iterator
apparaît est quand this
est utilisé dans une const
méthode:
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
fait this
const, ce qui rend this->v
const.
Vous pouvez généralement l'oublier avec auto
, mais si vous commencez à passer ces itérateurs, vous devrez y penser pour les signatures de méthode.
Tout comme const et non-const, vous pouvez facilement convertir de non-const en const, mais pas l'inverse:
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;
Lequel utiliser: analogue à const int
vs int
: préférez les itérateurs const chaque fois que vous pouvez les utiliser (lorsque vous n'avez pas besoin de modifier le conteneur avec eux), pour mieux documenter votre intention de lire sans modifier.
(comme d'autres l'ont dit) const_iterator ne vous permet pas de modifier les éléments sur lesquels il pointe, c'est utile dans les méthodes de classe const. Cela vous permet également d'exprimer votre intention.
ok Permettez-moi de l'expliquer avec un exemple très simple d'abord sans utiliser un itérateur constant considérons que nous avons une collection d'entiers aléatoires collection "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;
Comme on peut le voir pour écrire / éditer des données à l'intérieur de la collection, l'itérateur normal est utilisé mais pour la lecture, un itérateur constant a été utilisé. Si vous essayez d'utiliser un itérateur constant dans la première boucle for, vous obtiendrez une erreur. En règle générale, utilisez un itérateur constant pour lire les données à l'intérieur de la collection.