В чем разница между const_iterator и non-const iterator в С++ STL?

В чем разница между const_iterator и iterator и где вы будете использовать один над другим?

Ответ 1

const_iterator не позволяют изменять значения, на которые они указывают, регулярные iterator do.

Как и все вещи на С++, всегда предпочитайте const, если нет веских оснований для использования регулярных итераторов (т.е. вы хотите использовать тот факт, что они не const, чтобы изменить указанное значение).

Ответ 2

Они должны быть очень понятны. Если итератор указывает на элемент типа 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_iterator - это итератор, который указывает на элемент const, поэтому, хотя сам итератор может быть обновлен (например, увеличен или уменьшен), элемент, который он указывает, не может быть изменен.

Ответ 3

К сожалению, многие методы для контейнеров STL принимают итераторы вместо const_iterators как параметры. Поэтому, если у вас есть const_iterator, вы не можете сказать "вставить элемент перед элементом, на который указывает этот итератор" (например, на мой взгляд, это не концептуально нарушение const). Если вы хотите это сделать, вам нужно преобразовать его в неконстантный итератор, используя std:: advance() или boost:: next(). Например. boost:: next (container.begin(), std:: distance (container.begin(), the_const_iterator_we_want_to_unconst)). Если контейнер является std:: list, тогда время выполнения этого вызова будет O (n).

Таким образом, универсальное правило для добавления const везде, где это "логично", делает его менее универсальным, когда дело касается контейнеров STL.

Однако в бункере-контейнерах принимают константы (например, boost:: unordered_map:: erase()). Поэтому, когда вы используете форсированные контейнеры, вы можете быть "const agressive". Кстати, кто-нибудь знает, будут ли или когда контейнеры STL исправлены?

Ответ 4

Используйте const_iterator, когда можете, используйте iterator, когда у вас нет другого выбора.

Ответ 5

(как говорили другие) const_iterator не позволяет вам изменять элементы, на которые он указывает, это полезно внутри методов класса const. Это также позволяет вам выразить свое намерение.

Ответ 6

ok Позвольте мне объяснить это с помощью очень простого примера, не используя постоянный итератор рассмотрим, что мы собираем набор случайных целых "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;

Как видно для записи/редактирования данных внутри коллекции, используется обычный итератор, но для чтения используется постоянный итератор. Если вы попытаетесь использовать константный итератор в первом цикле, вы получите ошибку. В качестве правила большого пальца используйте постоянный итератор для чтения данных внутри коллекции.

Ответ 7

Минимальные примеры

Инициаторы, не являющиеся константами, позволяют изменять то, на что они указывают:

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

Итераторы констант не выполняют:

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 делает this const, что делает this->v const.

Обычно вы можете забыть об этом с помощью auto, но если вы начнете передавать эти итераторы, вам нужно будет подумать о них для сигнатур метода.

Как и 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 int vs int: предпочитайте константные итераторы всякий раз, когда вы можете их использовать (когда вам не нужно изменять контейнер с ними), чтобы лучше документировать ваше намерение читать без изменения.