Antworten:
const_iterator
Mit s können Sie die Werte, auf die sie zeigen, nicht ändern iterator
.
Wie bei allen Dingen in C ++ immer bevorzugen const
, es sei denn, es gibt einen guten Grund, reguläre Iteratoren zu verwenden (dh Sie möchten die Tatsache nutzen, dass sie const
den angegebenen Wert nicht ändern).
Sie sollten so ziemlich selbsterklärend sein. Wenn der Iterator auf ein Element vom Typ T zeigt, zeigt const_iterator auf ein Element vom Typ 'const T'.
Es entspricht im Grunde den Zeigertypen:
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
Ein const-Iterator zeigt immer auf dasselbe Element, daher ist der Iterator selbst const. Das Element, auf das es zeigt, muss jedoch nicht const sein, sodass das Element, auf das es zeigt, geändert werden kann. Ein const_iterator ist ein Iterator, der auf ein const-Element verweist. Während der Iterator selbst aktualisiert werden kann (z. B. inkrementiert oder dekrementiert), kann das Element, auf das er zeigt, nicht geändert werden.
const iterater
und const_iterator
.
Leider verwenden viele Methoden für die STL-Container Iteratoren anstelle von const_iterators als Parameter. Wenn Sie also einen const_iterator haben , können Sie nicht sagen "Ein Element vor dem Element einfügen, auf das dieser Iterator zeigt" (so etwas zu sagen ist meiner Meinung nach konzeptionell keine Konstantenverletzung). Wenn Sie dies trotzdem tun möchten, müssen Sie es mit std :: advanced () oder boost :: next () in einen nicht konstanten Iterator konvertieren . Z.B. boost :: next (container.begin (), std :: distance (container.begin (), the_const_iterator_we_want_to_unconst)) . Wenn der Container eine std :: -Liste ist , beträgt die Laufzeit für diesen Aufruf O (n) .
Daher ist die universelle Regel, const hinzuzufügen, wo immer dies "logisch" ist, weniger universell, wenn es um STL-Container geht.
Boost-Container benötigen jedoch const_iterators (z. B. boost :: unordered_map :: erase ()). Wenn Sie also Boost-Container verwenden, können Sie "const agressive" sein. Weiß übrigens jemand, ob oder wann die STL-Container repariert werden?
vector
und macht das deque
Einfügen eines Elements alle vorhandenen Iteratoren ungültig, was nicht sehr ist const
. Aber ich verstehe deinen Standpunkt. Solche Operationen sind durch Container const
-ness geschützt , nicht durch die Iteratoren. Und ich frage mich, warum es in der Standard-Container-Schnittstelle keine Iterator-Konvertierungsfunktion von Konstante zu Nicht-Konstante gibt.
int const * foo;
int * const foo;
und int const * const foo;
alle drei sind gültig und nützlich, jeder auf seine Weise. std::vector<int> const bar
sollte das gleiche sein wie das zweite, aber es wird leider oft wie das dritte behandelt. Die Hauptursache des Problems ist, dass wir nicht sagen können, std::vector<int const> bar;
wann es keine Möglichkeit gibt, den gleichen Effekt wie int const *foo;
in einem Vektor zu erzielen .
Minimale ausführbare Beispiele
Mit nicht konstanten Iteratoren können Sie ändern, worauf sie verweisen:
std::vector<int> v{0};
std::vector<int>::iterator it = v.begin();
*it = 1;
assert(v[0] == 1);
Konst Iteratoren nicht:
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;
Wie oben gezeigt, v.begin()
ist const
überladen und gibt entweder iterator
oder const_iterator
abhängig von der Konstanz der Containervariablen zurück:
Ein häufiger Fall, in dem const_iterator
Popups angezeigt werden, this
ist die Verwendung innerhalb einer const
Methode:
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
macht this
const, was this->v
const macht .
Normalerweise können Sie es mit vergessen auto
, aber wenn Sie anfangen, diese Iteratoren weiterzugeben, müssen Sie für die Methodensignaturen darüber nachdenken.
Ähnlich wie bei const und non-const können Sie problemlos von non-const in const konvertieren, aber nicht umgekehrt:
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;
Welches zu verwenden ist: Analog zu const int
vs int
: Bevorzugen Sie konstante Iteratoren, wann immer Sie sie verwenden können (wenn Sie den Container nicht mit ihnen ändern müssen), um Ihre Absicht des Lesens ohne Änderung besser zu dokumentieren.
(wie andere gesagt haben) Mit const_iterator können Sie die Elemente, auf die es zeigt, nicht ändern. Dies ist innerhalb der const-Klassenmethoden nützlich. Sie können damit auch Ihre Absicht zum Ausdruck bringen.
ok Lassen Sie es mich zunächst anhand eines sehr einfachen Beispiels erklären, ohne einen konstanten Iterator zu verwenden. Denken Sie daran, dass wir eine Sammlung von zufälligen Ganzzahlen haben.
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;
Wie zum Schreiben / Bearbeiten von Daten innerhalb der Sammlung zu sehen ist, wird ein normaler Iterator verwendet, aber zu Lesezwecken wurde ein konstanter Iterator verwendet. Wenn Sie versuchen, zuerst einen konstanten Iterator für die Schleife zu verwenden, wird eine Fehlermeldung angezeigt. Verwenden Sie als Faustregel einen konstanten Iterator, um Daten in der Sammlung zu lesen.