Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
ООПиП - задания к lab2.doc
Скачиваний:
11
Добавлен:
01.03.2016
Размер:
249.34 Кб
Скачать

Итераторы в стиле stl

Итераторы в стиле STL стали доступны, начиная с версии Qt 2.0. Они совместимы с базовыми алгоритмами Qt и STL и оптимизированы по скорости.

Для каждого контейнерного класса есть два типа итераторов в стиле STL: один из них предоставляет доступ только для чтения, а другой - доступ для чтения-записи. Итераторы только для чтения должны использоваться везде, где это только возможно, так как они быстрее, чем итераторы для чтения-записи.

Контейнеры

Итераторы только для чтения

Итераторы для чтения-записи

QList<T>, QQueue<T>

QList<T>::const_iterator

QList<T>::iterator

QLinkedList<T>

QLinkedList<T>::const_iterator

QLinkedList<T>::iterator

QVector<T>, QStack<T>

QVector<T>::const_iterator

QVector<T>::iterator

QSet<T>

QSet<T>::const_iterator

QSet<T>::iterator

QMap<Key, T>, QMultiMap<Key, T>

QMap<Key, T>::const_iterator

QMap<Key, T>::iterator

QHash<Key, T>, QMultiHash<Key, T>

QHash<Key, T>::const_iterator

QHash<Key, T>::iterator

API итераторов в стиле STL сделан по образцу указателей в массиве. Например, оператор ++ перемещает итератор к следующему элементу, а оператор * возвращает элемент, на который позиционирован итератор. Фактически, для QVector и QStack, хранящих свои элементы в смежных ячейках памяти, тип iterator - это всего лишь typedef для T *, а тип const_iterator - всего лишь typedef для const T *.

В этом обсуждении мы сконцентрируемся на QList и QMap. Типы итераторов для QLinkedList, QVector и QSet имеют точно такой же интерфейс, что и итераторы QList; аналогично, типы итераторов для QHash имеют тот же интерфейс, что и итераторы QMap.

Вот типичный пример цикла для перебора всех элементов QList<QString> по порядку и конвертирования их в нижний регистр:

QList<QString> list;

list << "A" << "B" << "C" << "D";

QList<QString>::iterator i;

for (i = list.begin(); i != list.end(); ++i)

*i = (*i).toLower();

В отличие от итераторов в стиле Java, итераторы в стиле STL указывают непосредственно на элемент. Функция контейнера begin() возвращает итератор, указывающий на первый элемент контейнера. Функция контейнера end() возвращает итератор, указывающий на воображаемый элемент, находящийся в позиции, следующей за последним элементом контейнера. end() обозначает несуществующую позицию; он никогда не должен разыменовываться. Обычно, он используется, как условие выхода из цикла. Если список пуст, то begin() равен end(), поэтому цикл никогда не выполнится.

На диаграмме ниже красными стрелками показаны возможные позиции итератора в списке, содержащем четыре элемента:

При переборе элементов в обратном порядке с помощью итераторов в стиле STL, требуется, чтобы оператор декремента использовался перед обращения к элементу. Вот требуемый цикл while:

QList<QString> list;

list << "A" << "B" << "C" << "D";

QList<QString>::iterator i = list.end();

while (i != list.begin()) {

--i;

*i = (*i).toLower();

}

В этих фрагментах кода, мы использовали унарный оператор * для восстановления значения элемента (типа QString), хранящегося в некоторой позиции итератора, а затем для него вызывали QString::toLower(). Большинство компиляторов C++ (но не все) также позволяют писать i->toLower()().

Для доступа к элементам только для чтения, можно использовать const_iterator, constBegin() и constEnd(). Например:

QList<QString>::const_iterator i;

for (i = list.constBegin(); i != list.constEnd(); ++i)

qDebug() << *i;

В следующей таблице подводится итог API итераторов в стиле STL:

Выражение

Поведение

*i

Возвращает текущий элемент

++i

Перемещает итератор к следующему элементу

i += n

Перемещает итератор вперед на n элементов

--i

Перемещает итератор на один элемент назад

i -= n

Перемещает итератор назад на n элементов

i - j

Возвращает количество элементов, находящихся между итераторами i и j

Оба оператора ++ и -- могут использоваться и как префиксные (++i, --i) и как постфиксные (i++, i--) операторы. Префиксная версия изменяет итератор, и возвращает ссылку на измененный итератор; постфиксная версия, берет копию итератора перед его изменением, и возвращает эту копию. В выражениях, в которых возвращаемое значение игнорируется, мы рекомендуем использовать префиксную версию (++i, --i), так как она несколько быстрее.

Для неконстантных итераторов, возвращаемое значение унарного оператора * может быть использовано с левой стороны от оператора присваивания.

Для QMap и QHash, оператор * возвращает компонент значения элемента. Если вы хотите извлечь ключ, вызовите key() для итератора. Для симметрии, типы итераторов предоставляют также функцию value(), извлекающую значение. Например, здесь показано, как мы можем напечатать все элементы в QMap в консоль:

QMap<int, int> map;

...

QMap<int, int>::const_iterator i;

for (i = map.constBegin(); i != map.constEnd(); ++i)

qDebug() << i.key() << ":" << i.value();

Благодаря неявному совместному использованию данных, использование значений контейнера весьма недорого. API Qt содержит множество функций, возвращающих QList или QStringList со значениями (например, QSplitter::sizes()). Если вы хотите перебрать эти значения с помощью итератора в стиле STL, то вы всегда должны иметь копию контейнера и перебирать ее элементы. Например:

// ПРАВИЛЬНО

const QList<int> sizes = splitter->sizes();

QList<int>::const_iterator i;

for (i = sizes.begin(); i != sizes.end(); ++i)

...

// НЕПРАВИЛЬНО

QList<int>::const_iterator i;

for (i = splitter->sizes().begin();

i != splitter->sizes().end(); ++i)

...

Эта проблема не должна возникать при использовании функций, которые возвращают константный или неконстантный указатель на контейнер.

Неявное совместное использование данных имеет и другое влияние на использование итераторов в стиле STL: вы не должны делать копии контейнера, если для него активны неконстантные итераторы. Итераторы в стиле Java не страдают таким ограничением.