Чим відрізняється const_iterator від ітератора non-const у С ++ STL?


139

Яка різниця між a const_iteratorі an iteratorі де ви б використовували одне над іншим?


1
Ну, ім'я const_iterator звучить як ітератор const, тоді як річ, на яку він вказує ітератор, - це фактична const.
talekeDskobeDa

Відповіді:


124

const_iterators не дозволяють вам змінювати значення, на які вони вказують, регулярні iterators.

Як і всі речі в C ++, завжди віддайте перевагу const, якщо тільки немає поважних причин використовувати звичайні ітератори (тобто ви хочете використовувати той факт, що вони не constзмінюють вказане значення).


8
У досконалому світі це було б так. Але з C ++ const так само хороший, як і людина, яка написала код :(
JaredPar

мутація існує з дуже вагомих причин. Він використовується рідко, і, дивлячись на пошук у коді Google, виявляється неабиякий відсоток дійсного використання. Ключове слово є дуже потужним інструментом оптимізації, і це не так, як його видалення поліпшить коректність коректування ( кашлюк-штуршок-курець-референс )
coppro

2
Більше схожий на потужний хак. З усіх випадків, коли я коли-небудь бачив використання ключового слова "mutable", всі, окрім одного, були точним показником того, що код був погано написаний і потрібний мутант як хак, щоб уникнути дефектів.
Джон Дайблінг

7
Він має легітимне використання, як кешування результатів довгого обчислення в рамках const-класу. З іншого боку, це майже єдиний раз, коли я використовував мутант протягом майже двадцяти років розвитку C ++.
Head Geek

Узагальненим прикладом використання const_iterator може бути, коли ітератор є рецензією
talekeDskobeDa

40

Вони в значній мірі повинні бути зрозумілими. Якщо ітератор вказує на елемент типу T, то const_iterator вказує на елемент типу 'const T'.

Це в основному еквівалентно типам вказівника:

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

Ітератор const завжди вказує на один і той же елемент, тому ітератор сам const. Але елемент, на який він вказує, не повинен бути const, тому елемент, на який він вказує, може бути змінений. Const_iterator - це ітератор, який вказує на елемент const, тому, хоча сам ітератор може бути оновлений (наприклад, нарощений або зменшений), елемент, на який він вказує, не може бути змінений.


1
"Ітератор const завжди вказує на один і той же елемент." Це неправильно.
Джон Дайблінг

2
Як так? Зверніть увагу на відсутність підкреслення. Я протиставляю змінну типу const std :: вектор <T> :: ітератор зі std :: вектор <T> :: const_iterator. У першому випадку ітератор сам по собі const, тому його неможливо змінити, але елемент, на який він посилається, може змінюватися вільно.
джелф

4
А, бачу. Так, я пропустив підкреслення.
Джон Дайблінг

3
@JohnDibling Оновлено для пояснення тонкощів між const iteraterта const_iterator.
legends2k

8

На жаль, багато методів для контейнерів STL приймають ітератори замість const_iterators як параметри. Отже, якщо у вас є const_iterator , ви не можете сказати "вставити елемент перед елементом, на який вказує цей ітератор" (мовляв, така думка концептуально не є порушенням const). Якщо ви все-таки хочете зробити це, вам доведеться перетворити його на ітератор без const за допомогою std :: advance () або boost :: next () . Напр. boost :: next (container.begin (), std :: відстань (container.begin (), the_const_iterator_we_want_to_unconst)) . Якщо контейнер - це список std :: list , то час виконання цього виклику буде O (n) .

Тож універсальне правило додавати const там, де це "логічно" робити, є менш універсальним, якщо мова йде про контейнери STL.

Однак бутові контейнери приймають const_iterators (наприклад, boost :: unordered_map :: erase ()). Тож, коли ви використовуєте збільшити контейнери, ви можете бути «агресивними». До речі, хтось знає, чи або коли будуть виправлені контейнери STL?


1
Це може бути питанням думки. У випадку vectorта dequeвставлення одного елемента недійсні всі існуючі ітератори, що не дуже const. Але я бачу вашу думку. Такі операції захищені контейнером const, а не ітераторами. І мені цікаво, чому в стандартному інтерфейсі контейнера не існує функції перетворення const-to-nonconst ітератора.
Potatoswatter

Ви правильно Potatoswatter, я категоричний, це питання думки для випадкових контейнерів доступу і container.begin () + (the_const_iterator_we_want_to_unconst - container.begin ()) є O (1) в будь-якому випадку. Мені також цікаво, чому для контейнерів без випадкового доступу немає функції перетворення, але, можливо, є вагома причина? Чи знаєте ви, чи є якась причина, що функції контейнерів для невипадкового доступу не приймають const_iterators ?
Магнус Андермо

"сказати таке, на мою думку, концептуально не є порушенням const" - це дуже цікавий коментар, у мене є такі думки з цього приводу. За допомогою простих покажчиків можна сказати, int const * foo; int * const foo;і int const * const foo;всі три є дійсними та корисними, кожен по-своєму. std::vector<int> const barмає бути таким же, як другий, але, на жаль, часто трактується як третій. Першопричиною проблеми є те, що ми не можемо сказати, std::vector<int const> bar;коли означає, що немає способу отримати такий же ефект, як int const *foo;у векторі.
dgnuff

5

Використовуйте const_iterator, коли зможете, використовуйте ітератор, коли у вас немає іншого вибору.


4

Мінімальні приклади для запуску

Інетератори без участі дозволяють змінювати те, на що вони вказують:

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

Ітератори const не:

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;

Як було показано вище, v.begin()буде constперевантажена, і повертає або iteratorабо в const_iteratorзалежності від сталою-ності змінної контейнера:

Поширений випадок, коли const_iteratorвискакує, коли thisвикористовується всередині 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робить thisconst, що робить this->vconst.

Зазвичай ви можете забути про це auto, але якщо ви почнете передавати ці ітератори навколо, вам потрібно буде подумати про них для підпису методу.

Так само, як const та non-const, ви можете легко конвертувати з non-const в const, але не навпаки:

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;

Який з них використовувати: аналог const intvs int: віддавайте перевагу ітераторам const кожного разу, коли ви можете їх використовувати (коли вам не потрібно змінювати контейнер з ними), щоб краще документувати свій намір читати, не змінюючи.


0

(як вже говорили інші) const_iterator не дозволяє змінювати елементи, на які він вказує, це корисно всередині методів класів const. Це також дозволяє висловити свої наміри.


0

ОК, дозвольте спочатку пояснити це дуже простим прикладом, не використовуючи постійний ітератор, врахуйте, що у нас є збірка випадкових цілих чисел "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;

Як видно для запису / редагування даних всередині колекції, використовується звичайний ітератор, але для читання використовується постійний ітератор. Якщо ви спершу спробуєте використовувати постійний ітератор для циклу, ви отримаєте помилку. Як правило, використовуйте постійний ітератор для читання даних всередині колекції.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.