Политика конфиденциальностиКонтактыО сайтеОтзывыGitHubDonate
© EVILEG 2015-2018
Рекомендует хостинг
TIMEWEB

Qt/C++ - Урок 079. foreach или range-based циклы for в C++11?

Qt, foreach, for

В Qt имеется своё ключевое слово foreach для итерации по элементам контейнеров. Данное ключевое слово было введено ещё до стандарта C++11 и является макросом. На данный момент в стандарте C++11 присутствуют range-based циклы for , которые выполняют такой же функционал, как и foreach .

Но в обоих случаях есть свои нюансы. Давайте разберёмся.

К каким объектам могут быть применены

foreach и range-based for работают с контейнерами, которые имеют итератор. В данном случае различий нет. Их можно применить только к объектам с итератором.

Применение, программный код

Посмотрим на программный код. Допустим у нас имеется некий контейнер

QList<QString> strings;

И мы хотим что-то сделать с элементами этого контейнера, тогда запись для foreach будет выглядеть так

foreach (QString& str, strings)
{
    // ToDo something
}

а для range-based for запись будет следующей

for (QString& str : strings)
{
    // ToDo something
}

Препроцессор

Ключевое слово foreach является макросом, который в итоге будет эквивалентен вот такой записи

for (QList<QString>::iterator it = strings.begin(); it != strings.end(); ++it) 
{
    QString &str = *it;
    // ваш код
}

Но поскольку это макрос, то получаем оверхед по времени компиляции на обработку файлов препроцессором, который будет разворачивать все эти макросы в рабочий код.

Скорость работы

foreach работает медленнее range-based for циклов, поскольку выполняет предварительное копирование контейнера, о чём говорится в документации Qt. Из копирования вытекает следующий нюанс, что если во время работы foreach вы что-то сделаете с контейнером, то это не будет иметь эффекта на элементы в цикле. При этом неявное копирование осуществляется и тогда, когда вы не модифицируете элементы контейнера.

Подобное поведение кода может быть довольно затратным для STL контейнеров, которые дороги в копировании. Поэтому уже по этой причине лучше использовать range-based for циклы из стандарта C++11.

Недостатки range-based for

Если вчитаться в документацию Qt, то можно обнаружить, что они рекомендуют всё-таки использовать foreach для контейнеров Qt, а для всех остальных (STL, Boost и т.д.) использовать range-based for циклы. Мотивируется это тем, что контейнер с разделяемыми данными при работе range-based for цикла может быть отсоединён форсированно (detach) от этих общих разделяемых данных и при этом не будет возвращён к исходному состоянию, после выполнения действий, в результате чего может произойти повреждение данных.

Одним из таких классов неявно разделямых классов является QPen, который использует глубокое копирование при отсоединении (detach).

void QPen::setStyle(Qt::PenStyle style)
{
    detach();           // detach from common data
    d->style = style;   // set the style member
}

void QPen::detach()
{
    if (d->ref != 1) {
        ...             // perform a deep copy
    }
}

Заключение

По своей практике я не сталкивался с такими проблемами при использовании range-based for циклов для контейнеров Qt, полагаю, возможно это связано с тем, что не приходилось часто использовать подобные циклы для контейнеров с объектами QPen или иными классами Qt требующими отделения от разделяемых данных.

По факту последнее время использование контейнеров STL мне кажется более удобным, чем контейнеров Qt в связи с использование функторов (лямбд) для поиска (std:;find, std::find_if алгоритмы и т.д.) конкретных объектов в контейнере, вместо обычно цикла с условием в нём.

Также придерживаюсь точки зрения, что лучше использовать конструкции языка вместо МАКРОСОВ. Просто нужно учитывать, что могут быть проблемы, в связи с особенностями контейнеров и других классов Qt.

А так итоговое решение по использованию foreach vs range-based for оставляю на ваше усмотрение.

Комментарии

Только авторизованные пользователи могут публиковать комментарии.
Пожалуйста, авторизуйтесь или зарегистрируйтесь
v
17 января 2019 г. 11:51
vitalir12

C++ - Тест 004. Указатели, Массивы и Циклы

  • Результат:20баллов,
  • Очки рейтинга-10
v
17 января 2019 г. 11:49
vitalir12

C++ - Тест 002. Константы

  • Результат:50баллов,
  • Очки рейтинга-4
v
17 января 2019 г. 11:13
vitalir12

C++ - Тест 003. Условия и циклы

  • Результат:28баллов,
  • Очки рейтинга-10
Последние комментарии
I
16 января 2019 г. 8:06
IscanderChe

Заработало. Забыл model->select(); вписать.
I
16 января 2019 г. 8:02
IscanderChe

Всё равно пусто, хотя строка с данными в базу добавляется.
16 января 2019 г. 7:51
Евгений Легоцкой

потому, что нужно сохранять информацию для всех остальных ролей и столбцов через вызов переопределённого метода. Да к тому же вы ещё и зациклили вызов метода data. QVariant MySqlTableModel:...
I
16 января 2019 г. 7:43
IscanderChe

Сделал вот так. В tableView ничего нет, кроме заголовка. QVariant MySqlTableModel::data(const QModelIndex &index, int role) const{ if (role == Qt::DisplayRole) { QTime ...
Сейчас обсуждают на форуме
18 января 2019 г. 11:26
nayk1982

Для Desktop делал так: void pause(int ms){ QTimer timer; timer.setInterval( qBound(1, ms, 3600000) ); timer.setSingleShot(true); QEventLoop loop; QObject::connect(&...
17 января 2019 г. 12:01
Алексей Внуков

у меня просто есть отдельное поле с чекбоксамими какие колонки нужно отображать CheckBox { id: checkBox text: qsTr("some text") checked: true onC...
15 января 2019 г. 16:53
Михаиллл

Спасибо, заработало.Но выдало обычный текст без форатирования HTML.Придется искать дальше
15 января 2019 г. 12:52
BlinCT

Я же вам выше написал CLion умеет работать с ремоут машинами. И Qt так же собирает.
Присоединяйтесь к нам в социальных сетях

Для зарегистрированных пользователей на сайте присутствует минимальное количество рекламы