KnigaRead.com/
KnigaRead.com » Компьютеры и Интернет » Программирование » Жасмин Бланшет - QT 4: программирование GUI на С++

Жасмин Бланшет - QT 4: программирование GUI на С++

На нашем сайте KnigaRead.com Вы можете абсолютно бесплатно читать книгу онлайн Жасмин Бланшет, "QT 4: программирование GUI на С++" бесплатно, без регистрации.
Перейти на страницу:

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

01 using namespace std;

02 void sineTable(vector<double> &vect)

03 {

04 vect.resize(360);

05 for (int i = 0; i < 360; ++i)

06 vect[i] = sin(i / (2 * M_PI));

07 }

В результате вызов будет не столь простым и менее понятным:

vector<double> table;

sineTable(table);

В Qt применяется неявное совместное использование данных во всех ее контейнерах и во многих других классах, включая QByteArray, QBrush, QFont, QImage, QPixmap и QString. Это делает применение этих классов очень эффективным при передаче по значению, как аргументов функции, так и возвращаемых функциями значений.

Неявное совместное использование данных в Qt гарантирует, что данные не будут копироваться, если мы их не модифицируем. Чтобы получить максимальные выгоды от применения этой технологии, необходимо выработать в себе две новые привычки при программировании. Одна связана с использованием функции at() вместо оператора [ ] при доступе только для чтения к (неконстантному) вектору или списку. Поскольку при применении Qt—контейнеров нельзя сказать, оказывается ли [ ] с левой стороны оператора присваивания или нет, предполагается самое худшее и принудительно выполняется действительное копирование (deep сору), в то время как at() не допускается в левой части оператора присваивания.

Подобная проблема возникает при прохождении контейнера с помощью итераторов в стиле STL. Когда вызываются функции begin() или end() для неконстантного контейнера, Qt всегда принудительно выполняет действительное копирование при совместном использовании данных. Решение, позволяющее избавиться от этой неэффективности, состоит в применении по мере возможности const_iterator, constBegin() и constEnd().

В Qt предусмотрен еще один, последний метод прохода по элементам последовательного контейнера — оператор цикла foreach. Он выглядит следующим образом:

QLinkedList<Movie> list;

foreach (Movie movie, list) {

if (movie.title() == "Citizen Kane") {

cout << "Found Citizen Kane" << endl;

break;

}

}

Псевдоключевое слово foreach реализуется с помощью стандартного цикла for. На каждом шаге цикла переменная цикла (movie) устанавливается на новый элемент, начиная с первого элемента контейнера и затем двигаясь вперед. Цикл foreach автоматически использует копию контейнера при входе в цикл, и по этой причине модификации контейнера в ходе цикла не влияют на сам цикл.

Поддерживаются операторы цикла break и continue. Если тело цикла состоит из одного оператора, необязательно указывать скобки. Как и для оператора for, переменная цикла может определяться вне цикла, например:

QLinkedList<Movie> list;

Movie movie;

foreach (movie, list) {

if (movie.title() == "Citizen Kane") {

cout << "Found Citizen Kane" << endl;

break;

}

}

Определение переменной цикла вне цикла — единственная возможность для контейнеров, содержащих типы данных с запятой (например, QPair<QString, int>).

Как работает неявное совместное использование данных

Неявное совместное использование данных работает автоматически и незаметно для пользователя, поэтому нам не надо в программном коде предусматривать специальные операторы для обеспечения этой оптимизации. Но поскольку хочется знать, как это работает, мы рассмотрим пример и увидим, что скрывается от нашего внимания. В этом примере используются строки типа QString — одного из многих неявно совместно используемых Qt—классов:

QString str1 = "Humpty";

QString str2 = str1;

Мы присваиваем переменной str1 значение «Humpty» (Humpty-Dumpty — Шалтай—Болтай) и переменную str2 приравниваем к переменной str1. К этому моменту оба объекта QString ссылаются на одну и ту же внутреннюю структуру данных в памяти. Кроме символьных данных эта структура данных имеет счетчик ссылок, показывающий, сколько строк QString ссылается на одну структуру данных. Поскольку обе переменные ссылаются на одни данные, счетчик ссылок будет иметь значение 2.

str2[0] = 'D';

Когда мы модифицируем переменную str2, выполняется действительное копирование данных, чтобы переменные str1 и str2 ссылались на разные структуры данных и их изменение приводило к изменению их собственных копий данных. Счетчик ссылок данных переменной str1 («Humpty») принимает значение 1, и счетчик ссылок данных переменной str2 («Dumpty») тоже принимает значение 1. Значение 1 счетчика ссылок означает, что данные не используются совместно.

str2.truncate(4);

Если мы снова модифицируем переменную str2, никакого копирования не будет происходить, поскольку счетчик ссылок данных переменной str2 имеет значение 1. Функция truncate() непосредственно обрабатывает значение переменной str2, возвращая в результате строку «Dump». Счетчик ссылок по-прежнему имеет значение 1.

str1 = str2;

Когда мы присваиваем строку str2 строке str1, счетчик ссылок для данных str1 снижается до 0 и приводит к тому, что теперь никакая строка типа QString не содержит значения «Humpty». Память освобождается. Обе строки QStrings теперь ссылаются на значение «Dump», счетчик ссылок которого теперь имеет значение 2.

Часто не пользуются возможностью совместного использования данных в многопоточных программах из-за условий гонок при доступе к счетчикам ссылок. В Qt этой проблемы не возникает. Классы—контейнеры используют инструкции ассемблера при реализации атомарных операций со счетчиками. Эта технология доступна пользователям Qt через применение классов QSharedData и QSharedDataPointer.

Ассоциативные контейнеры

Ассоциативный контейнер содержит произвольное количество элементов одинакового типа, индексируемых некоторым ключом. Qt содержит два основных класса ассоциативных контейнеров: QМар<К, T> и QHash<K, T>.

QMap<K, T> — это структура данных, которая содержит пары ключ—значение, упорядоченные по возрастанию ключей. Такая организация данных обеспечивает хорошую производительность операций поиска и вставки, а также при проходе данных в порядке их сортировки. Внутренне QMap<K, T> реализуется как слоеный список (skip—list).

Рис. 11.6. Ассоциативный массив, связывающий QString с int.

Простой способ вставки элементов в ассоциативный массив состоит в использовании функции insert():

QMap<QString, int> map;

map.insert("eins", 1);

map.insert("sieben", 7);

map.insert("dreiundzwanzig", 23);

Можно поступить по-другому — просто присвоить значение заданному ключу:

map["eins"] = 1;

map["sieben"] = 7;

map["dreiundzwanzig"] = 23;

Оператор [ ] может использоваться как для вставки, так и для поиска. Но если этот оператор используется для поиска значения, для которого не существует ключа, будет создан новый элемент с данным ключом и пустым значением. Чтобы не создавать случайно пустые элементы, вместо оператора [ ] можно использовать функцию value():

int val = map.value("dreiundzwanzig");

Если ключ отсутствует в ассоциативном массиве, возвращается значение по умолчанию, создаваемое стандартным конструктором данного типа значений. Для базовых типов и указателей возвращается нуль. Мы можем определить другое значение, используемое по умолчанию, с помощью второго аргумента функции value(), например:

int seconds = map.value("delay", 30);

Это эквивалентно следующим операторам:

int seconds = 30;

if (map.contains("delay"))

seconds = map.value("delay");

Типы данных К и T в ассоциативном массиве QMap<K, T> могут быть базовыми типами (например, int и double), указатели и классы, которые имеют стандартный конструктор, конструктор копирования и оператор присваивания. Кроме того, тип К должен обеспечивать оператор operator < (), поскольку QMap<K, T> применяет его для хранения элементов в порядке возрастания значений ключей.

Класс QMap<K, T> имеет две удобные функции, keys() и values(), которые особенно полезны при работе с небольшими наборами данных. Они возвращают списки типа QList ключей и значений ассоциативного массива.

Перейти на страницу:
Прокомментировать
Подтвердите что вы не робот:*