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





(7)


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;

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

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




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




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




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

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

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




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

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

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




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




Вот еще одна ссылка, демонстрирующая, как const не является безопасным версией или релевантным для ссылочных типов.

Резюме :

  • Значение вашего свойства const устанавливается во время компиляции и не может меняться во время выполнения
  • Константа не может быть помечена как статическая - ключевое слово обозначает, что они являются статическими, в отличие от полей readonly, которые могут.
  • Const не может быть ничего, кроме значений (примитивных) типов
  • Ключевое слово readonly помечает поле как неизменяемое. Однако свойство может быть изменено внутри конструктора класса
  • Ключевое слово только для чтения можно также комбинировать со статикой, чтобы заставить его действовать так же, как const (по крайней мере на поверхности). Существует заметная разница, когда вы смотрите на IL между двумя
  • константные поля помечены как «буквальные» в IL, а readonly - «initonly»,




c++ stl iterator const