Quelle est la difference entre a const_iterator
et iterator
et où utiliseriez-vous l’une sur l’autre?
const_iterator
s ne vous permet pas de changer les valeurs qu’ils désignent, mais les iterator
s ordinaires le font.
Comme pour tout ce qui se passe en C++, préférez toujours const
, sauf s’il existe une bonne raison d’utiliser des itérateurs classiques (c’est-à-dire que vous voulez utiliser le fait qu’ils ne sont pas const
pour changer le pointeur. valeur).
Ils devraient à peu près s'expliquer d'eux-mêmes. Si l'itérateur pointe sur un élément de type T, alors const_iterator pointe sur un élément de type 'const T'.
C'est fondamentalement équivalent aux types de pointeur:
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 constant pointe toujours vers le même élément, donc l'itérateur lui-même est const. Mais l'élément vers lequel il pointe n'a pas besoin d'être const, il peut donc être modifié. Un const_iterator est un itérateur qui pointe vers un élément const. Ainsi, même si l'itérateur lui-même peut être mis à jour (incrémenté ou décrémenté, par exemple), l'élément vers lequel il pointe ne peut pas être modifié.
Malheureusement, de nombreuses méthodes pour les conteneurs STL prennent itérateurs au lieu de const_iterators en tant que paramètres. Donc, si vous avez un const_iterator, vous ne pouvez pas dire "insérez un élément avant l'élément que cet itérateur pointe vers" (à mon avis, une telle chose n'est pas une violation constante, à mon avis). Si vous voulez quand même le faire, vous devez le convertir en un itérateur non-constant 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 conteneur est un std :: list, le temps d'exécution de cet appel sera alors O (n).
Ainsi, la règle universelle d'ajouter const chaque fois que cela est "logique" est moins universelle en ce qui concerne les conteneurs STL.
Cependant, les conteneurs boost prennent const_iterators (par exemple, boost :: unordered_map :: erase ()). Ainsi, lorsque vous utilisez des conteneurs boost, vous pouvez être "const agressif". À propos, est-ce que quelqu'un sait si ou quand les conteneurs STL seront réparés?
Utilisez const_iterator chaque fois que vous le pouvez, utilisez itérateur lorsque vous n'avez pas d'autre choix.
Exemples minimaux
Les itérateurs non constants vous permettent de modifier ce qu'ils désignent:
std::vector<int> v{0};
std::vector<int>::iterator it = v.begin();
*it = 1;
assert(v[0] == 1);
Les itérateurs constants 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 renvoie iterator
ou const_iterator
En 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 méthode const
:
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
rend this
const, ce qui rend this->v
const.
Vous pouvez généralement oublier cela avec auto
, mais si vous commencez à passer ces itérateurs, vous devrez y penser pour les signatures de méthodes.
Tout comme const et non-const, vous pouvez convertir facilement 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 constants 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.
ok Permettez-moi de l'expliquer avec un exemple très simple d'abord sans utiliser 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 l'écriture/l'édition de données à l'intérieur de la collection, on utilise l'itérateur normal, mais pour la lecture, on utilise un itérateur constant. Si vous essayez d'utiliser l'itérateur constant dans la première boucle, vous obtiendrez une erreur. En règle générale, utilisez itérateur constant pour lire les données dans la collection.
(comme d'autres l'ont déjà dit) const_iterator ne vous permet pas de modifier les éléments sur lesquels il pointe, c'est utile dans les méthodes de la classe const. Cela vous permet également d’exprimer votre intention.