Читаем QT 4: программирование GUI на С++ полностью

Допускающие запись итераторы (mutable iterators) имеют функции для вставки, модификации и удаления элементов в ходе просмотра контейнеров. В показанном ниже цикле из списка удаляются отрицательные числа:

QMutableListIterator i(list);

while (i.hasNext) {

if (i.next < 0.0)

i.remove;

}

Функция remove всегда работает с последним пройденным элементом. Она так же ведет себя при проходе элементов в обратном направлении:

QMutableListIterator i(list);

i.toBack;

while (i.hasPrevious) {

if (i.previous < 0.0)

i.remove;

}

Аналогично допускающие запись итераторы в стиле Java имеют функцию setValue, которая модифицирует последний пройденный элемент. Ниже показано, как можно заменить отрицательные числа их абсолютным значением:

QMutableListIterator i(list);

while (i.hasNext) {

int val = i.next;

if (val < 0.0)

i.setValue(-val);

}

Кроме того, можно вставлять элемент в текущую позицию итератора с помощью функции insert. После этого итератор перемещается в позицию между новым элементом и следующим за ним.

Кроме итераторов в стиле Java каждый класс последовательных контейнеров C имеет итераторы в стиле STL двух типов: С<Т>::iterator и C::const_iterator. Они отличаются тем, что итератор const_iterator не позволяет модифицировать данные.

Функция контейнера begin возвращает итератор в стиле STL, ссылающийся на первый элемент контейнера (например, list[0]), в то время как функция контейнера end возвращает итератор, ссылающийся на элемент «после последнего элемента» (например, list[5] для списка размером 5). Если контейнер пустой, функции begin и end возвращают одинаковое значение. Это может использоваться для проверки наличия хотя бы одного элемента в контейнере, хотя для этой цели более удобно пользоваться функцией isEmpty.

Рис. 11.5. Допустимые позиции итераторов в стиле STL.

Синтаксис применения итераторов в стиле STL моделирует синтаксис применения указателей С++. Мы можем использовать операторы ++ и —— для перехода на следующий или предыдущий элемент, а также унарный оператор * для извлечения значения элемента из позиции текущего итератора. Для вектора vector типы итераторов iterator и const_iterator определяются просто как typedef для Т * и const T *. (Так можно делать, поскольку QVector хранит свои элементы в последовательных адресах памяти.)

В показанном ниже примере каждое значение в списке QList заменяется своим абсолютным значением:

QList::iterator i = list.begin;

while (i ! = list.end) {

*i = qAbs(*i);

++i;

}

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

QList list = splitter->sizes;

QList::const_iterator i = list.begin;

while (i != list.end) {

do_something(*i);

++i;

}

Ниже дается пример неправильного программного кода:

// Неправильный программный код

QList::const_iterator i = splitter->sizes.begin;

while (i != splitter->sizes.end) {

do_something(*i);

++i;

}

Это происходит из-за того, что функция QSplitter::sizes возвращает новый список QList по значению при каждом новом своем вызове. Если мы не сохраняем возвращенное функцией значение, С++ автоматически удалит его еще до начала итерации, оставляя нам «повисший» итератор. Дело еще усугубляется тем, что на каждом новом шаге цикла функция QSplitter::sizes должна генерировать новую копию списка из-за вызова функции splitter->sizes.end. Поэтому используйте общее правило: когда применяются итераторы в стиле STL, всегда следует обрабатывать в цикле копию экземпляра контейнера, возвращаемого по значению.

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

QListIterator i(splitter->sizes);

while (i.hasNext) {

do_something(i.next);

}

Перейти на страницу:
Нет соединения с сервером, попробуйте зайти чуть позже