Программирование с Qt:
Часть 3. Контейнеры
Часть 3. Контейнеры
Источник:
IBM developerWorks Россия
Автор:
Алексей Бешенов, технический писатель, независимый специалист
Уровень сложности: средний
В этой статье:
- 1. Контейнеры в Qt
- 2. Контейнеры с последовательным хранением элементов
- 3. Стек и очередь
- 4. Контейнеры с доступом по ключу
- 5. Множества
- 6. Итераторы
- Заключение
- Ресурсы
- Об авторе
Qt – кроссплатформенный инструментарий для разработки прикладного программного обеспечения, широко используемый при создании графических пользовательских интерфейсов. В первом материале цикла мы рассмотрели основные инструменты разработчика, а также используемую в Qt объектную модель. Теперь перейдем к вопросам, касающиеся типов, вариантов, подсчета ссылок и разделения данных, поскольку без этих базовых сведений работать с инструментарием невозможно.
1. Контейнеры в Qt
Qt предоставляет свои реализации строк, контейнеров и алгоритмов в качестве упрощенной кроссплатформенной альтернативы для
STL
Как и в STL, контейнеры Qt используют шаблоны C++ и позволяют хранить элементы нужного типа. Например,QLinkedList<T>
– шаблон связного списка; если требуется связный список целых чисел, то используетсяQLinkedList<int>
.
Для контейнеров применяется неявное разделение памяти. Передача контейнеров в виде аргументов и их возврат не связаны с затратами, так как копия будет создаваться лишь при необходимости изменения одного из объектов:
QList<T> list1; |
По возможности, лучше передавать const-ссылки, так как в этом случае изменений гарантированно не будет.
У всех контейнеров есть некоторые общие методы:
int size() const; // число элементов |
Также везде перегружены операторы сравнения == и !=. Подразумевается, что для типа элементов будет перегружен оператор ==. Контейнеры с последовательным хранением элементов сравниваются с учетом порядка, остальные контейнеры порядок не учитывают.
2. Контейнеры с последовательным хранением элементов
Для начала рассмотрим контейнерыQList<T>, QVector<T>
иQLinkedList<T>
.
Для добавления элементов к такому контейнеру либо для объединения контейнеров можно использовать операторы +, +=, <<:
QList<int> xs, ys, zs, us; |
В этих контейнерах выделяются первый и последний элементы. Ссылки на них можно получить при помощиfirst()
иlast()
:
T& first(); |
(Для совместимости с STL поддерживаются также имена методовfront()
иback()
.)
Прежде чем вызывать эти методы, убедитесь, что контейнер не пустой.
Также можно сверить значение первого или последнего элемента:
bool startsWith (const T& val) const; |
Поиск элементов:
bool contains (const T& val) const; // содержится ли val в контейнере? |
Добавление и удаление:
void prepend (const T& val); // добавить val в начало |
Для вставки и удаления используются итераторы. Пока мы только перечислим соответствующие методы, но если вы не знаете, что такое итераторы и как они работают, то подробные объяснения содержатся в последнем разделе.
iterator insert (iterator before, const T& val); |
Далее будут рассмотрены различия между QList<T>, QVector<T> и QLinkedList<T>.
2.1. Контейнеры с доступом по индексу
QList<T>
иQVector<T>
используют доступ по индексу за постоянное время O(1). Как и в массивах C++, элементы нумеруются с 0.
Доступ по индексу:
const T& at (int i) const; |
Замена значения по индексу:
void replace (int i, const T& val) |
При обращении к элементам по индексу проверяйте, что индекс лежит в интервале [0, size()).
Поиск индекса:
int indexOf (const T& val, int from = 0) const; |
Вставка в данной позиции:
void insert (int i, const T& val); |
Если нужно копировать n элементов из середины, начиная с позиции pos, то используется mid (pos,n). При n = -1 элементы копируются до конца:
QList<T> mid (int pos, int length = -1) const; |
2.1.1. QList
QList<T>
– наиболее часто используемый контейнер.
Вставка элементов в середину списка осуществляется за O(n). Для вставки за постоянное время в Qt имеется связный списокQLinkedList
.
Амортизированное время вставки элементов в начало и в конец – O(1).
Особые методыQList<T>
:
T QLinkedList::takeFirst(); // вернуть первый элемент и удалить |
Пример работы со списком:
QList<int> list; |
2.1.2. QVector
QVector<T>
– это обычный динамический массив. Его имеет смысл использовать, если элементы должны храниться в одном участке памяти.
При использовании вектора можно получить указатель на данные и обращаться с ними как с обычным массивом:
T* QVector::data(); |
Конечно, указатели действуют только до тех пор, пока содержимое не будет перемещено в памяти.
При создании вектора можно указать размер и инициализирующее значение:
QVector::QVector (int size = 0, const T& val = T()); |
Если потом потребуется снова инициализировать вектор, то используетсяfill()
:
QVector<T>& QVector::fill (const T& val); // инициализировать |
Помимо числа элементовsize()
, у вектора имеется емкость – общий зарезервированный объем.
int QVector::capacity() const; |
Число элементов меняется при помощиresize()
:
void QVector::resize (int size); |
Емкость меняется при помощиreserve()
:
void QVector::reserve (int n); |
Емкость можно устанавливать, если заранее известно максимальное число элементов. Если емкости не хватит для увеличения размера на некотором этапе, то это лишь затронет быстродействие.
Неиспользуемая память освобождается при помощиsqueeze()
:
void QVector::squeeze(); |
Как иreserve()
, этот метод может потребоваться в редких случаях при оптимизации кода.
Удаление и вставка в векторе:
QVector<T>::iterator QVector::insert (iterator before, int count, const T& val); |
Пример работы с вектором:
QVector<int> vec(5,23); |
2.2. QLinkedList
QLinkedList<T>
– связный список. Он отличается отQList<T>
тем, что при работе для доступа к элементам нужно использовать итераторы. При этом вставка элементов в середину происходит за постоянное время O(1) и не приводит к порче итератора, указывающего на некоторый другой элемент. Доступ по индексу осуществляется за O(n).
Специфические методыQList<T>
:
T QLinkedList::takeFirst(); // вернуть первый элемент и удалить |
Пример работы со связным списком:
QLinkedList<QString> list; |
3. Стек и очередь
3.1. QStack
Стек (LIFO)QStack<T>
реализован через наследование отQVector<T>
с добавлением следующих методов:
T& QStack::top(); // верхний элемент |
Прежде чем брать элемент, убедитесь, что стек не пустой(!stack.isEmpty())
.
Пример работы со стеком:
QStack<QString> stack; |
3.2. QQueue
Очередь (FIFO)QQueue<T>
реализована через наследование отQList<T>
с добавлением следующих методов:
T& QQueue::head(); // головной элемент |
Прежде чем брать элемент, убедитесь, что очередь не пустая(!queue.isEmpty())
.
Пример работы с очередью:
QQueue<QString> queue; |
4. Контейнеры с доступом по ключу
4.1. QHash
QHash<K,T>
– хэш-таблица, отображающая ключи типаK
в значения типаT
.
Амортизированное время поиска и вставки –O(1)
.
Если требуется структура, в которой элементы хранятся отсортированными по ключу, используйтеQMap<K,T>
.
Добавление элементов:
QHash<K,T>::iterator QHash::insert (const K& key, const T& val); |
insert
(key, val) привязывает значение val за ключом key. Если такой ключ уже есть, то значение замещается. Если нужно хранить несколько значений для одного ключа, используйтеinsertMulti
(key, val). Кроме того, уQHash<K,T>
имеется специальный дочерний классQMultiHash<K,T>
.
Если нужно целиком вставить содержимое другой хэш-таблицы, используйтеunite()
:
QHash<K,T>& QHash::unite (const QHash<K,T>& other); |
Если текущая хэш-таблица уже содержит определенный ключ, то в результирующей таблице он будет дублироваться.
Доступ к значениям по ключу:
const T QHash::value (const K& key) const; |
Можно также извлечь ключи по значениям, но хэш-таблица не оптимизирована для работы в этом направлении, поэтому время поиска будет линейным.
const K QHash::key (const T& val) const; |
Если ключ не найден, то возвращается значение по умолчанию K(). Также можно использовать метод
const T QHash::value (const K& key, const T& defaultValue) const; |
– он возвращает defaultValue, если ключа нет.
Поиск элементов:
bool QHash::contains (const K& key) const; // есть ли ключ key? |
Если ключу соответствует несколько значений, то возвращается итератор, указывающий на последний добавленный элемент; если этот итератор инкрементировать, то можно получить другие значения. Если элемент не найден, то возвращается итераторend()
.
Удаление по итератору и по ключу:
QHash<K,T>::iterator QHash::erase (QHash<K,T>::iterator pos); |
Пример работы с хэш-таблицей:
QHash<QString,QString> hash; |
4.2. QMultiHash
QMultiHash<K,T>
наследуетQHash<K,T>
и ориентирован на структуры, в которых одному ключу может соответствовать несколько значений. Метод
QHash<K,T>::iterator QMultiHash::insert (const K& key, const T& val); |
всегда добавляет новый элемент, даже если ключ повторяется. Чтобы заменить имеющееся значение при повторении ключей, нужно вызывать
QHash<K,T>::iterator replace (const K& key, const T& val); |
Также добавляются методы для поиска и удаления элементов, принимающие, помимо ключа, соответствующее значение:
bool contains (const K& key, const T& val) const; |
Для слияния хэш-таблиц перегружены операторы + и +=:
QMultiHash QMultiHash::operator+ (const QMultiHash& other) const; |
4.3. QMap
QMap<K,T>
– ассоциативный массив, отображающий ключи типаK
в значения типаT
.
Элементы сортируются по ключу (для чего требуется перегрузкаoperator<())
, и проход поQMap
всегда дает содержимое в отсортированном порядке.
Поиск и вставка осуществляются за логарифмическое времяO(log n)
.
Если элементы не нужно сортировать по ключам, то используйтеQHash<K,T>
.
ИнтерфейсQMap<K,T>
практически совпадает сQHash<K,T>
. Имеются дополнительные функции для поиска элементов по ключам:
QHash<K,T>::iterator QHash::lowerBound (const K& key); |
lowerBound(key)
возвращает итератор, указывающий на первый элемент с ключомkey
. Если такого ключа нет, то возвращается итератор, указывающий на ближайший элемент с большим ключом.
upperBound(key)
возвращает итератор, указывающий на последний элемент с ключомkey
. Если такого ключа нет, то возвращается итератор, указывающий на ближайший элемент с большим ключом.
Таким образом, все элементы с данным ключом лежат в интервале[lowerBound, upperBound]
.
Пример:
QMultiMap<QString,QString> map; |
4.4. QMultiMap
QMultiMap<K,T>
наследуетQMap<K,T>
и ориентирован на структуры, в которых одному ключу может соответствовать несколько значений.
ИнтерфейсQMultiMap<K,T>
практически полностью соответствуетQMultiHash<K,T>
.
5. Множества
QSet<T>
– неупорядоченное множество, основанное на хэш-таблице. Множество позволяет быстро получать и добавлять значения:
bool QSet::contains (const T& val) const; |
Особый интерес представляют операции объединения, пересечения и разности:
QSet<T>& QSet::unite (const QSet<T>& other); |
Для тех же целей перегружены операторы:
// Объединение: |
Пример работы с множествами:
QSet<QString> x, y, z, u, v, w; |
6. Итераторы
Итераторы – объекты для унифицированного доступа к элементам контейнера.
Вы должны быть знакомы с итераторами, если имеете хороший опыт программирования на C++ с использованием STL, либо на Java. В Qt есть как итераторы в стиле STL, так и итераторы в стиле Java. Первые немного эффективнее, но со вторыми удобнее работать.
6.1. Итераторы в стиле STL
Итераторы в стиле STL эффективно реализованы (по сути, это синтаксический «сахар» для указателей), и через них работают алгоритмы<QtAlgorithms>
.
В каждом контейнере есть два типа итераторов:const_iterator
с доступом только для чтения иiterator
с доступом для чтения и записи (таблица 1).
Таблица 1. Итераторы в стиле 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<T>, QMultiMap<T> | QMap<T>::const_iterator | QMap<T>::iterator |
QHash<K,T>, QMultiHash<K,T> | QHash<K,T>::const_iterator | QHash<K,T>::iterator |
Операции с итераторами записываются так же, как и для указателей. Для QVector<T> итераторы могут непосредственно сводиться к указателям:
typedef T* iterator; |
Операции над итераторами в стиле STL перечислены в таблице 2. Переход на произвольное число элементов вперед и назад, сравнение итераторов и подсчет числа элементов между ними поддерживаются только итераторами с произвольным доступом –QList
иQVector
. У других итераторов доступен только переход на одну позицию вперед или назад. Ссылка может быть как константной, так и с возможностью изменения значения, в зависимости от типа итератора (const_iterator
илиiterator
).
Таблица 2. Операции над итераторами в стиле STL
*i | Ссылка на текущий элемент. |
---|---|
i + n | Итератор, указывающий на позицию n элементов вперед. |
++i | Перейти к следующему элементу. |
i += n | Перейти на n элементов вперед. |
i - n | Итератор, указывающий на позицию n элементов назад. |
--i | Перейти к предыдущему элементу. |
i -= n | Перейти на n элементов назад. |
i - j | Число элементов между позициями i и j. |
i < j |
true , если итератор i предшествует j. |
i[n] | То же самое, что и *(i+n) . |
В дополнение к указанным префиксным записям ++i и --i, доступны также постфиксные i++ и i--, но обычно возвращаемые значения игнорируются. Поэтому префиксный вариант эффективнее, так как в постфиксном варианте итератор копируется перед изменением, и возвращается ссылка на копию.
Метод контейнераbegin()
возвращает итератор, указывающий на первый элемент контейнера;end()
возвращает итератор, указывающий на позицию после последнего элемента.end()
разыменовывать нельзя.
constBegin()
иconstEnd()
делают то же самое, но возвращают const-итераторы.
iterator begin(); |
Таким образом, проход по элементам выглядит следующим образом:
QList<int> list; |
Здесь*i
разыменовывает итератор. Для контейнера над объектами некоторого классаFoo
с методомFoo::bar()
можно было бы положиться на сходство итераторов с указателями и писать код в таком духе:
for (i = list.begin(); i != list.end(); ++i) |
Но это будет работать не всегда. Нужно явно указывать разыменование.

Дляiterator
разыменованному итератору можно присваивать новые значения. Если элементы контейнера не изменяются, то используетсяconst_iterator
.
ДляQMap
иQHash
разыменование итератора дает значение. Чтобы получить ключ, используйте метод итератораkey()
. Значение можно получить при помощиvalue()
.
QHash<QString,QString> hash; |
Если вы изменяете не только элементы контейнера, но и сам контейнер (добавляете или удаляете из него элементы), то нужно быть внимательным, чтобы не допустить ошибок. Например, требуется удалить из списка чисел элементы, превышающие 10.
Используем методerase
:
iterator QList::erase (iterator i) |
Он удаляет элемент, на который указывает итератор i, и возвращает итератор, указывающий на следующий элемент, либоend()
, если удаленный элемент – последний.
Можно попробовать сделать так:
QList<int> list; |
Это неправильный код! Сначала будет удалено значение 12, аerase(i)
вернет итератор, указывающий на значение 20, но затем итератор будет инкрементирован, и мы пропустим 20. В самом конце списка мы вообще перейдем черезend()
.
Правильное решение:
QList<int>::iterator i = list.begin(); |
Итерация в прямом и обратном направлении – не симметричные операции. Сравните следующий код:
i = list.begin(); |
В STL имеются еще два типа итераторов,reverse_iterator
иconst_reverse_iterator
. В Qt их нет.
Синтаксис STL-итераторов может быть неудобным или служить источником ошибок.
QList<Foo*>::iterator i; |
Так как в Qt используется неявное разделение данных, то в API можно встретить методы, возвращающие контейнеры по значению – это не приводит к большим затратам. Но это служит источником ошибок при работе с итераторами в стиле STL. Если у нас есть метод
QList<T> Foo::bar() const; |
то следующий код неправильный:
for (i = foo.bar().begin(); i != foo.bar().end(); ++i) |
– здесьbegin()
иend()
вызывается для разных объектов!
Также неявное разделение приводит к тому, что контейнер нельзя копировать, если на нем действуют итераторы с доступом для записи. Например:
QList<int> list1; |
Неявное разделение должно означать, что копирование не приводит к дублированию данных до тех пор, пока этого не требуется. По логике вещей, при изменении содержимогоlist1
должна создаваться глубокая копияlist2
, чтобы второй контейнер не затрагивался. Но если это производится через STL-итератор, то просто меняется содержимое обоих контейнеров.
Эту проблему, как и проблему асимметричности прохода в прямом и обратном направлениях, решают итераторы в стиле Java.
6.2. Итераторы в стиле Java
Итераторы в стиле Java немного уступают в эффективности итераторам в стиле STL, однако более удобны для программиста.
Аналогично, для каждого класса (таблица 3) есть итератор с доступом только для чтения (например,QListIterator<T>
) и итератор с доступом для чтения и записи (соответственно,QMutableListIterator<T>
).
Таблица 3. Итераторы в стиле Java
Контейнер | const-итератор | Итератор |
---|---|---|
QList<T>, QQueue<T> | QListIterator<T> | QMutableListIterator<T> |
QLinkedList<T> | QLinkedListIterator<T> | QMutableLinkedListIterator<T> |
QVector<T>, QStack<T> | QVectorIterator<T> | QMutableVectorIterator<T> |
QSet<T> | QSetIterator<T> | QMutableSetIterator<T> |
QMap<T>, QMultiMap<T> | QMapIterator<T> | QMutableMapIterator<T> |
QHash<K,T>, QMultiHash<K,T> | QHashIterator<K,T> | QMutableHashIterator<K,T> |
QListIterator<T>
имеет более короткое имя, чемQMutableListIterator<T>
, потому что чаще используются итераторы с доступом только для чтения.
Сравните сQList<int>::const_iterator
иQList<int>::iterator
– в STL короче записывается имя итератора с доступом для записи.
В отличие от итераторов в стиле STL, итераторы в стиле Java указывают не на сами элементы, а на позицию до первого элемента, между двумя элементами, либо за последним элементом. Методprevious()
возвращает ссылку на предыдущий элемент, аnext()
– на следующий:
const T& QListIterator::previous(); |

Определить, есть ли элемент до или после итератора, позволяют методыhasPrevious()
иhasNext()
.
bool hasPrevious() const; |
Таким образом, типичный проход по списку выглядит так:
QList<int> list; |
Здесь конструктору итератора передается список. В самом начале он указывает на точку перед его первым элементом. Вызов next() в цикле переводит итератор на следующую позицию – после первого элемента и перед вторым, и т.д.
Если проход нужно осуществить в обратном порядке, то при помощи методаtoBack()
итератор нужно перевести в позицию за последним элементом, а затем использоватьprevious()
:
QList<int> list; |
Операции над итераторами в стиле Java перечислены в таблице 4.
Таблица 4. Операции над итераторами в стиле Java
toFront() | Перейти к позиции до первого элемента. |
---|---|
toBack() | Перейти к позиции за последним элементом. |
hasNext() | Возвращаетtrue , если итератор не находится за последним элементом. |
next() | Возвращает ссылку на следующий элемент и переводит итератор на следующую позицию. |
peekNext() | Возвращает ссылку на следующий элемент, не изменяя итератор. |
hasPrevious() | Возвращаетtrue , если итератор не находится до первого элемента. |
previous() | Возвращает ссылку на предыдущий элемент и переводит итератор на предыдущую позицию. |
peekPrevious() | Возвращает ссылку на предыдущий элемент, не изменяя итератор. |
У итератора с доступом для записи есть дополнительные методы:
void QMutableListIterator::insert (const T& x); |
insert (x)
вставляет x в текущую позицию и переводит итератор в позицию после нового элемента.
remove()
удаляет последний элемент, через который перешел итератор, т.е. предыдущий, если вызывалсяnext()
, и следующий, если вызывалсяprevious()
.
setValue (x)
устанавливает значение x для последнего элемента, через который перешел итератор.
Вернемся к примеру с удалением элементов из списка:
QList<int> list; |
Код выглядит более просто и компактно, чем в случае сQList<int>::iterator
, так как удаление элемента не портит итератор. То же самое можно сделать через обход от конца к началу:
QMutableListIterator<int> i(list); |
Все итераторы имеют одинаковый интерфейс, но соответствующие методыQMapIterator
иQHashIterator
возвращают объектItem
, для которогоkey()
дает ключ, аvalue()
– значение.
Item QMapIterator::next(); |
key()
возвращает ссылку на ключ последнего элемента, через который прошел итератор, аvalue()
– ссылку на его значение.
const K& QMapIterator::key() const; |
(Аналогично для итератора с доступом для записи, но ссылка на значение будетT&
, а неconst T&
)
QHash<QString,QString> hash; |
Для поиска значений используютсяfindNext()
иfindPrevious()
.
bool findNext (const T& x); |
findNext(x)
ищет значение, начиная с текущей позиции итератора и до конца контейнера. Если значение найдено, то итератор устанавливается после соответствующего элемента и возвращаетсяtrue
. Если значение не найдено, то итератор устанавливается за последний элемент контейнера и возвращаетсяfalse
.
findPrevious(x)
ищет значение, начиная с текущей позиции итератора и до начала контейнера. Если значение найдено, то итератор устанавливается до соответствующего элемента и возвращаетсяtrue
. Если значение не найдено, то итератор устанавливается до первого элемента контейнера и возвращаетсяfalse
.
ВQMap
иQHash
поиск производится по значению (поиск по ключу реализован самими контейнерами).
Вы могли уже убедиться, что с итераторами в стиле Java работать удобнее. Единственный их недостаток – они дают больший исполняемый код. Поэтому при необходимости оптимизации по скорости работы и размеру исполняемых файлов можно остановиться на STL-итераторах.
6.3. foreach
Для прохода по всем элементам контейнера в Qt имеется ключевое словоforeach
.
QList<int> list; |
Вforeach
на каждом шаге числу x будет присваиваться значение очередного элемента списка. При этом возможные изменения x не будут затрагивать содержимое списка. (Для изменения содержимого используйте итераторы.)
Но имейте в виду, что внутриforeach
реализован через препроцессор C++, поэтому следующий код не сработает:
QList<QPair<int,QString> > list; |
Запятая всегда разделяет аргументы макроса, поэтому не может разделять аргументы шаблона, служащего аргументом макроса. Правильный вариант:
QPair<int,QString> e; |
Также в Qt имеется ключевое словоforever
, которое соответствует бесконечному циклуfor(;;)
.
Если в настройках указатьCONFIG += no_keywords
, то вместо ключевых словforeach
иforever
можно использовать макросыQ_FOREACH
иQ_FOREVER
.
Заключение
В Qt имеются следующие контейнеры с последовательным хранением элементов:
- Список
QList<T>
. - Вектор
QVector<T>
(динамический массив). - Связный список
QLinkedList<T>
.
Среди них наиболее часто используетсяQList<T>
.QVector<T>
необходим для хранения данных в непрерывном массиве, аQLinkedList<T>
оптимизирован для частой вставки элементов в середину.
Кроме того:
- через
QVector<T>
реализован стекQStack<T>
, - через
QList<T>
реализована очередьQQueue<T>
.
Для хранения пар «ключ-значение» имеются:
-
QHash<K,T>, QMultiHash<K,T>
. -
QMap<K,T>, QMultiMap<K,T>
.
Отличие состоит в том, что элементыQMap
содержатся в отсортированном виде.QMultiHash
иQMultiMap
– дочерние классы, интерфейс которых ориентирован на структуры с несколькими значениями для одного ключа.
Для унифицированного доступа используются итераторы. Итераторы в стиле Java более удобны, но итераторы в стиле STL работают более эффективно.
В следующей статье мы рассмотрим алгоритмы<QtAlgorithms>
, а также работу с флагами и массивами битов.
Ресурсы
- Примите участие в обсуждении материала на форуме IBM developerWorks.
- Qt Software (EN).
- Документация по Qt (EN).
- Qt Centre (EN).
- Информационный бюллетень «Qt Quarterly» (EN).
- Списки рассылки Qt (EN).
- Blanchette Jasmin, Mark Summerfield.
C++ GUI Programming with Qt 4 (2nd Edition) (EN). Prentice Hall: 2006. – 752 pp. - Daniel Molkentin.
The Book of Qt 4: The Art of Building Qt Applications (EN). No Starch Press: 2007. – 440 pp. - J. Thelin.
Foundations of Qt Development (EN). Apress: 2007. – 528 pp. - Bjarne Stroustrup.
The C++ Programming Language (Special Edition) (EN). Addison-Wesley: 2000 – 1040 pp. - Meyers Scott. Effective STL: 50 Specific Ways to Improve Your Use of the Standard Template Library. – Addison-Wesley, 2001. – 288 pp.
- Ж. Бланшет, М. Саммерфилд. Qt 4. Программирование GUI на C++. 2-е издание. Кудиц-пресс: 2008 – 736 с.
- Б. Страуструп. Язык программирования C++. 2-е издание. Бином: 2007 – 1099 с.
- Мейерс С. Эффективное использование STL. – СПб.: Питер, 2002. – 224 с.