В чем разница между const_iterator
и iterator
и где вы будете использовать один над другим?
В чем разница между const_iterator и non-const iterator в С++ STL?
Ответ 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
в зависимости от константы контейнерной переменной:
- Как begin() знает, какой возвращаемый тип возвращать (const или не const)?
- Как работает перегрузка функций const и non-const?
Обычный случай, когда 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
: предпочитайте константные итераторы всякий раз, когда вы можете их использовать (когда вам не нужно изменять контейнер с ними), чтобы лучше документировать ваше намерение читать без изменения.