цикл for для Qt 'foreach' в C ++ - PullRequest
       26

цикл for для Qt 'foreach' в C ++

45 голосов
/ 21 апреля 2009

Что лучше (или быстрее): цикл C ++ for или оператор foreach, предоставляемый Qt? Например, следующее условие

QList<QString> listofstrings;

Что лучше?

foreach(QString str, listofstrings)
{
    //code
}

или

int count = listofstrings.count();
QString str = QString();
for(int i=0;i<count;i++)
{
    str = listofstrings.at(i);
    //Code
}

Ответы [ 11 ]

148 голосов
/ 21 апреля 2009

Это действительно не имеет значения в большинстве случаев.

Большое количество вопросов в StackOverflow относительно того, является ли этот метод или этот метод более быстрым, несмотря на тот факт, что в подавляющем большинстве случаев код тратит большую часть своего времени на ожидание действий пользователей.

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

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

Получите ваш рабочий код first . Затем включите его fast (и только если вы обнаружите фактическую проблему с производительностью).

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

22 голосов
/ 21 апреля 2009

Прежде всего, я просто хотел бы сказать, что я согласен с Паксом, и что скорость, вероятно, не влияет на это. foreach выигрывает, основываясь на удобочитаемости, и этого достаточно в 98% случаев.

Но, конечно, ребята из Qt изучили это и фактически провели некоторое профилирование: http://blog.qt.io/blog/2009/01/23/iterating-efficiently/

Главный урок, который следует извлечь из этого, заключается в следующем: используйте константные ссылки в циклах только для чтения, поскольку это позволяет избежать создания временных экземпляров. Это также делает цель цикла более явной, независимо от используемого вами метода цикла.

15 голосов
/ 21 апреля 2009

Это действительно не имеет значения . Скорее всего, если ваша программа работает медленно, это не проблема. Однако следует отметить, что вы не проводите абсолютно равное сравнение. Qt's foreach больше похоже на это (в этом примере будет использоваться QList<QString>):

for(QList<QString>::iterator it = Con.begin(); it != Con.end(); ++it) {
    QString &str = *it;
    // your code here
}

Макрос может сделать это, используя некоторые расширения компилятора (например, GCC __typeof__), чтобы получить тип переданного контейнера. Также представьте, что Boost's BOOST_FOREACH очень похож на концепцию.

Причина, по которой ваш пример несправедлив, заключается в том, что ваша не-Qt версия добавляет дополнительную работу.

Вы индексируете, а не выполняете итерацию. Если вы используете тип с несмежным распределением (я подозреваю, что это может быть в случае QList<>), тогда индексирование будет более дорогим, поскольку код должен вычислять «где» n-й элемент.

Это, как говорится. Это все еще не имеет значения. Разница во времени между этими двумя частями кода будет незначительной, если она вообще существует. Не тратьте свое время на беспокойство. Напишите, что вы найдете более ясным и понятным.

РЕДАКТИРОВАТЬ: В качестве бонуса, в настоящее время я решительно поддерживаю версию итерации контейнера C ++ 11, она чиста, лаконична и проста:

for(QString &s : Con) {
    // you code here
}
10 голосов
/ 25 апреля 2009

Я не хочу отвечать на вопрос, который быстрее, но я хочу сказать, что лучше.

Самая большая проблема с forech в Qt заключается в том, что он берет копию вашего контейнера перед его итерацией. Вы можете сказать «это не имеет значения, потому что классы Qt пересчитаны», но поскольку используется копия, вы фактически не меняете свой оригинальный контейнер.

Таким образом, forech в Qt можно использовать только для циклов только для чтения, поэтому его следует избегать. Qt с радостью позволит вам написать цикл foreach, который, по вашему мнению, будет обновлять / модифицировать ваш контейнер, но в конце все изменения будут отброшены.

6 голосов
/ 16 августа 2017

Поскольку Qt 5.7 макрос foreach устарел, Qt рекомендует использовать вместо него C ++ 11 for.
http://doc.qt.io/qt-5/qtglobal.html#foreach

(подробнее о разнице здесь: https://www.kdab.com/goodbye-q_foreach/)

4 голосов
/ 21 апреля 2009

Во-первых, я полностью согласен с ответом, что «это не имеет значения». Выберите самое чистое решение и оптимизируйте, если оно станет проблемой.

Но другой способ взглянуть на это состоит в том, что часто самым быстрым решением является то, которое наиболее точно описывает ваши намерения. В этом случае foreach QT говорит, что вы хотите применить некоторые действия для каждого элемента в контейнере.

Обычный цикл говорит, что вы хотите счетчик i. Вы хотите многократно добавлять единицу к этому значению i, и, пока оно меньше количества элементов в контейнере, вы хотели бы выполнить какое-либо действие.

Другими словами, цикл for переопределяет проблему. Это добавляет много требований, которые на самом деле не являются частью того, что вы пытаетесь сделать. Вы не заботитесь о счетчике цикла. Но как только вы пишете цикл for, он должен быть там.

С другой стороны, сотрудники QT не дали никаких дополнительных обещаний, которые могут повлиять на производительность. Они просто гарантируют итерацию по контейнеру и применение действия к каждому из них.

Другими словами, зачастую самое чистое и элегантное решение также является самым быстрым.

3 голосов
/ 25 июля 2010

Эталон и его результаты по этому можно найти на http://richelbilderbeek.nl/CppExerciseAddOneAnswer.htm

ИМХО (и многие другие здесь) это (то есть скорость) не имеет значения.

Но не стесняйтесь делать свои собственные выводы.

3 голосов
/ 21 апреля 2009

foreach из Qt имеет более четкий синтаксис для цикла for IMHO, так что в этом смысле он лучше. С точки зрения производительности, я сомневаюсь, что в этом что-то есть.

Вы могли бы рассмотреть возможность использования BOOST_FOREACH вместо этого, поскольку это хорошо продуманный модный цикл, и он переносим (и, вероятно, когда-нибудь пробьется в C ++ и также пригоден для будущего).

2 голосов
/ 21 апреля 2009

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

Однако, для больших коллекций, for начнет бить foreach в какой-то момент. (при условии, что оператор 'at ()' эффективен.

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

1 голос
/ 21 апреля 2009

Я бы ожидал, что foreach будет работать номинально быстрее в некоторых случаях и примерно одинаково в других, за исключением случаев, когда элементы являются фактическим массивом, и в этом случае разница в производительности незначительна.

Если он реализован поверх перечислителя, он может быть более эффективным, чем прямое индексирование, в зависимости от реализации. Это вряд ли будет менее эффективным. Например, если кто-то выставляет сбалансированное дерево как индексируемое и перечислимое, тогда foreach будет прилично быстрее. Это связано с тем, что каждый индекс должен будет самостоятельно найти указанный элемент, в то время как перечислитель имеет контекст текущего узла для более эффективной навигации к следующему элементу.

Если у вас есть фактический массив, то от реализации языка и класса зависит, будет ли foreach быстрее для того же, что и для.

Если индексирование - это буквальное смещение памяти (например, C ++), тогда for должно быть немного быстрее, поскольку вы избегаете вызова функции. Если индексация - это косвенное обращение, как и вызов, то оно должно быть таким же.

Все это, как говорится ... Мне трудно найти здесь повод для обобщения. Это последний вид оптимизации, который вам следует искать, даже если в вашем приложении есть проблемы с производительностью. Если у вас есть проблема с производительностью, которая может быть решена путем изменения способа итерации, у вас действительно нет проблем с производительностью. У вас есть ОШИБКА, потому что кто-то написал либо действительно дурацкий итератор, либо действительно дрянной индексатор.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...