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

Подобное копирование контейнера может показаться неэффективным, но это не так из-за оптимизации посредством так называемого неявного совместного использования даннъис (implicit sharing). Это означает, что операция копирования Qt—контейнера выполняется почти так же быстро, как копирование одного указателя. Только если скопированная строка изменяется, тогда данные действительно копируются — и все это делается автоматически и незаметно для пользователя. Поэтому неявное совместное использование иногда называют «копированием при записи» (copy on write).

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

01 QVector sineTable

02 {

03 QVector vect(360);

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

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

06 return vect;

07 }

Вызов этой функции выглядит следующим образом:

QVector table = sineTable;

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

01 using namespace std;

02 void sineTable(vector &vect)

03 {

04 vect.resize(360);

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

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

07 }

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

vector 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 list;

foreach (Movie movie, list) {

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

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

break;

}

}

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

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

QLinkedList list;

Movie movie;

foreach (movie, list) {

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

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

break;

}

}

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

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