[Предыдущая: Что нового в Qt 4] [Начало] [Следующая: Структура Interview]
Классы-контейнеры Tulip
Qt 4 представляет новый набор контейнеров, заменяющих старый контейнер указателей QCollection и более новый, основанный на значениях QTL.
Общий обзор
Контейнеры Tulip заменяют собой QTL контейнеры Qt 3 (QValueList, QValueVector, QMap), но имеют ряд преимуществ:
- Контейнеры предоставляют новый улучшенный и более защищенный от ошибок синтаксис, чем STL, подобный Java-итераторам. (Итераторы стиля STL остаются как облегченная STL-совместимая альтернатива.)
- Контейнеры оптимизированы для минимизации набираемого кода.
- Пустой контейнер практически не занимает памяти, так как память требуется только для хранения указателя.
- Контейнеры вполне защищены, их можно свободно копировать даже между потоками без дополнительных формальностей. Они не требуют использования QDeepCopy.
Tulip предоставляет следующий набор контейнеров: QList, QLinkedList, QVector, QStack и QQueue. В большинстве приложений лучше всего использовать QList. Класс реализован в виде списка-массива, что позволяет быстро добавлять в начало и в конец. Если вам нужен настоящий связанный список, то используйте QLinkedList; если вы хотите размещать записи в памяти рядом, обратите внимание на QVector. QStack и QQueue - вспомогательные классы, предоставляющие семантику FIFO и LIFO.
Tulip также предоставляет следующие ассоциативные контейнеры: QMap, QMultiMap, QHash, QMultiHash и QSet. "Multi" контейнеры поддерживают ассоциирование нескольких значений с одним ключом. "Hash" контейнеры предоставляют быстрый доступ с использованием хэш-функции для бинарного поиска в отсортированном наборе.
Контейнеры Tulip поддерживают конструкцию foreach - Qt-специфичную добавку к языку C++, использующую стандартный C++ препроцессор. Её синтаксис:
foreach (переменная, контейнер)
выражение;
Пример:
QList<QString> list;
...
foreach (QString str, list)
cout << str.ascii() << endl;
Переменная-итератор может быть объявлена и вне цикла. Например:
QString str;
foreach (str, list)
cout << str.ascii() << endl;
Как и for, foreach поддерживает, break, continue и вложенность. Qt делает копию контейнера перед входом в цикл. Если вы измените контейнер во время выполнения цикла, то в цикле это не даст эффекта.
Для получения детальной информации о новых контейнерах смотрите обзоры Основные контейнеры и Основные алгоритмы.
В добавок к новым контейнерам значительно много усилий было вложено в QByteArray и QString. В Qt 3 класс QCString был подобен QByteArray. Новый QByteArray автоматически добавляет разделитель '\0' после последнего символа. Например, массив байтов размера 5 содержащий "abcde" имеет нулевой байт в позиции 5 (один байт дополнительно). Это устраняет все проблемы, возникавшие в Qt 3 при взаимных преобразованиях между QByteArray и QCString.
Во избежание ошибок QByteArray::data() никогда не возвращает нулевой указатель. Кроме того, ликвидировано различие между нулевым указателем и указателем на пустую строку. К примеру QByteArray() == QByteArray("") и QString() == QString("").
Примеры
Первая группа примеров показывает, как использовать итераторы в стиле Java. Основное различие между итераторами стиля Java и стиля STL заключается в том, что итератор стиля Java указывает на точку между записями (либо перед первой записью, либо после последней), тогда как итератор стиля STL указывает на запись (либо на "место после последней записи"). Основное преимущество итераторов в стиле Java является то, что перебор записей вперед и назад - действия симметричные.
Перебор записей в контейнере с помощью итераторов стиля Java:
// вперед // назад
QList<QString> list; QList<QString> list;
... ...
QListIterator<QString> i(list); QListIterator<QString> i(list);
while (i.hasNext()) i.toBack();
cout << i.next().ascii() << endl; while (i.hasPrev())
cout << i.prev().ascii() << endl;
Изменение записей с помощью итераторов стиля Java:
// вперед // назад
QMutableListIterator<int> i(list); QMutableListIterator<int> i(list);
while (i.hasNext()) i.toBack();
if (i.next() > 128) while (i.hasPrev())
i.setValue(128); if (i.prev() > 128)
i.setValue(128);
Удаление записей с помощью итераторов стиля Java:
// вперед // назад
QMutableListIterator<int> i(list); QMutableListIterator<int> i(list);
while (i.hasNext()) i.toBack();
if (i.next() % 2 != 0) while (i.hasPrev())
i.remove(); if (i.prev() % 2 != 0)
i.remove();
Специфика перебора записей с использованием итераторов в стиле STL и в стиле Java:
// стиль STL // стиль Java
QMap<int, QWidget *>::const_iterator i; QMapIterator<int, QWidget *> i(map);
for (i = map.begin(); i != map.end(); ++i) while (i.findNext(widget))
if (i.value() == widget) cout << "Found widget " << widget
cout << "Found widget " << widget << " under key "
<< " under key " << i.key() << endl;
<< i.key() << endl;
Модификация и удаление записей с использованием итераторов в стиле STL и в стиле Java:
// стиль STL // стиль Java
QList<int>::iterator i = list.begin(); QMutableListIterator<int> i(list);
while (i != list.end()) { while (i.hasNext()) {
if (*i == 0) { int val = i.next();
i = list.erase(i); if (val < 0)
} else { i.setValue(-val);
if (*i < 0) else if (val == 0)
*i = -*i; i.remove();
++i; }
}
}
Следующая группа примеров демонстрирует API для непосредственной работы с классами-контейнерами. API для работы с классами-контейнерами похож на классы QTL в Qt 3, но лучше во многих отношениях.
Перебор записей в QList с использованием индекса (подобный способ доступа является достаточно быстрым, даже для больших списков, т.к. QList реализован в виде списка-массива):
QList<double> list;
...
for (int i = 0; i < list.size(); ++i) {
if (list[i] < 0.0)
list[i] = 0.0;
}
Возвращение из словаря использует значение по умолчанию, если элемент с данным ключом отсутствует:
QMap<QString, int> map;
...
map.value("TIMEOUT", 30); // возвращает 30, если "TIMEOUT" не определён в словаре
Получение всех значений, ассоциированных с ключом в QMultiMap или QMultiHash:
QMultiMap<QString, int> map;
...
QList<int> values = map.values("TIMEOUT");
Сравнение с Qt 3
Контейнеры Tulip содержат значения. Если вы хотите использовать список в котором каждое значение - это QWidget *, используйте QList<QWidget *>.
Новые контейнеры не поддерживают автоудаления. Мы обнаружили, что на практике, единственным случаем, где нужно автоудаление является ситуация, где в контейнере содержатся значения, а не указатели (например, QList<int>, а не QList<int *>). Если вы хотите удалить все элементы, хранящиеся в контейнере, используйте qDeleteAll().
Если вы привыкли использовать QValueList в Qt 3, то в Qt 4 можете с легкостью заменить его на QList или QLinkedList. В большинстве случаев лучшим выбором является QList: как правило, он быстрее, требует меньшего кодирования и меньше памяти. Однако итераторы при работе с QLinkedList обеспечивают большую надежность, и только QLinkedList предоставляет независимые от времени методы вставки в середину списка, что может быть важно при работе со списками, содержащими тысячи элементов.
Если вы привыкли использовать QValueVector или QMap в Qt 3, то при переходе на Qt 4 подобные классы (QVector, QMap) предоставят вам схожую функциональность.
[Предыдущая: Что нового в Qt 4] [Начало] [Следующая: Структура Interview]
Copyright © 2009 Nokia Corporation and/or its subsidiary(-ies) |
Торговые марки |
Qt 4.5.3 |
|