Шаг 57.
Библиотека Qt.
Контейнерные классы

    На этом шаге рассмотрим контейнерные классы.

    Одна из самых распространенных задач в программировании заключается в организации обработки групп элементов. Реализация и отладка программ с использованием подобного рода структур отнимает у разработчиков много времени, т. к. они каждый раз вынуждены решать одни и те же задачи. Для решения этой проблемы библиотека контейнеров предоставляет набор часто используемых классов, на правильную работоспособность которых можно положиться. Это позволяет разработчику сконцентрироваться на реализации самого приложения и не вникать в детали реализации используемых контейнерных классов.

    Qt предоставляет библиотеку контейнеров, именуемую Tulip, являющуюся составной частью ядра Qt, поэтому ее понимание очень важно для дальнейшего изучения. Данная библиотека не только очень похожа на STL (Standard Template Library, стандартная библиотека шаблонов), но и совместима с ней. В данном случае, разработчик может свободно выбирать, чем ему лучше воспользоваться — Tulip или STL. Предпочтительнее выбрать первую библиотеку, т. к. Tulip является частью Qt и активно используется в ней. Второй аргумент в пользу применения Tulip — это то, что эта библиотека в соответствии со спецификой классов Qt оптимизирована по производительности и расходу памяти. Использование в программах шаблонных классов, на основе которых построены контейнеры, заметно увеличивает размер исполняемых программ. Это связано с тем, что в каждом объектном файле реализации класса, использующего контейнеры, находится созданный компилятором код контейнеров с нужными типами, и этот код может повторяться. Библиотека Tulip создавалась именно с учетом этих обстоятельств и, кроме того, оптимизирована для заметного уменьшения размера объектного кода.

    Реализация классов Tulip расположена в модуле QtCore. В основе библиотеки Tulip (как и в STL) лежат три понятия:

   Контейнерные классы

    Контейнерные классы — это классы, которые в состоянии хранить в себе элементы различных типов данных. Почти все контейнерные классы в Qt реализованы как шаблонные и, таким образом, они могут хранить данные любого типа. Основная идея шаблона состоит в создании родового класса, который определяется при создании объекта этого класса. Классы контейнеров могут включать целые серии других объектов, которые, в свою очередь, тоже могут являться контейнерами.

    Очень важно правильно понимать отличия разновидностей контейнеров, для того чтобы правильно подобрать контейнер для конкретного случая. От этого в значительной степени зависит скорость работы кода и эффективность использования памяти. Qt предоставляет две категории разновидностей классов контейнеров: последовательные (sequence containers) и ассоциативные (associative containters).

    Последовательные контейнеры — это упорядоченные коллекции, где каждый элемент занимает определенную позицию. Позиция зависит от места его вставки. К последовательным контейнерам относятся: вектор (vector), список (list), стек (stack) и очередь (queue). Соответственно, Qt содержит пять классов этой категории:

    Ассоциативные контейнеры — это коллекции, в которых позиция элемента зависит от его значения, т. е. после занесения элементов в коллекцию порядок их следования будет задаваться их значением. К ассоциативным контейнерам относятся: множество (set), словарь (map) и хэш (hash). Классы этой категории:

    Во всех контейнерах этих двух групп доступны операции, перечисленные в табл. 1.

Таблица 1. Операторы и методы, определенные во всех контейнерных классах
Оператор, метод
Описание
== и !=
Операторы сравнения, равно и не равно
=
Оператор присваивания
[ ]
Оператор индексации. Исключение составляют только классы QSet<T> и QLinkedList<T>, в нем этот оператор не определен
begin() и constBegin()
Методы, возвращающие итераторы, установленные на начало последовательности элементов контейнера. Для класса QSet<T> возвращаются только константные итераторы
end() и constEnd()
Методы, возвращающие константные итераторы, установленные на конец последовательности элементов контейнера
clear()
Удаление всех элементов контейнера
insert()
Операция вставки элементов в контейнер
remove()
Операция удаления элементов из контейнера
size() и count()
Оба метода идентичны — возвращают количество элементов контейнера, но применение первого предпочтительно, т. к. соответствует STL
value()
Возвращает значение элемента контейнера. В QSet<T> этот метод не определен
empty() и isEmpty()
Возвращают true, если контейнер не содержит ни одного элемента. Оба метода идентичны, но применение первого предпочтительно, т. к. соответствует STL


ПРИМЕЧАНИЕ. Важно не забывать о том, что классы, унаследованные от класса QObject, не имеют доступного конструктора копирования и оператора присваивания, т. к. они находятся в секции private. Следовательно, их объекты не могут храниться в контейнерах, поэтому нужно сохранять в контейнерах не сами объекты, наследуемые от класса QObject, а указатели на них.

    Если вам нужно проверить, содержит ли контейнер элементы или нет, то не применяйте для этого метод size(), а используйте метод empty(). Причина проста: метод empty() для всех контейнеров выполняется с постоянной сложностью, а size() может потребовать для контейнера списка линейных затрат, что может существенно снизить скорость вашего алгоритма.

    На следующем шаге рассмотрим итераторы.




Предыдущий шаг Содержание Следующий шаг