Evgenii Legotckoi
15 травня 2018 р. 12:26

Qt/C++ - Підручник 079. foreach проти діапазону для C++11?

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 залишаю на ваш розсуд.

Рекомендовані статті на цю тему

По статті запитували0питання

3

Вам це подобається? Поділіться в соціальних мережах!

a
  • 10 квітня 2020 р. 10:41

Since Qt 5.7 the foreach macro is deprecated, Qt encourages you to use the C++11 for instead.
http://doc.qt.io/qt-5/qtglobal.html#foreach

(more details about the difference here : https://www.kdab.com/goodbye-q_foreach/ )

Коментарі

Only authorized users can post comments.
Please, Log in or Sign up