Я начал с использования std::(w)string
исключительно контейнеров STL и преобразования в / из эквивалентов Qt, но я уже переключился QString
и обнаружил, что я использую контейнеры Qt все больше и больше.
Когда дело доходит до строк, он QString
предлагает гораздо более полную функциональность по сравнению с std::basic_string
Unicode. Он также предлагает эффективную реализацию COW , на которую я во многом положился.
Контейнеры Qt:
- предлагает ту же реализацию COW, что и в
QString
, что чрезвычайно полезно, когда речь идет об использовании foreach
макроса Qt (который выполняет копирование) и при использовании мета-типов или сигналов и слотов.
- можно использовать итераторы в стиле STL или итераторы в стиле Java
- стряхиваются с
QDataStream
- широко используются в API Qt
- иметь стабильную реализацию в операционных системах. Реализация STL должна подчиняться стандарту C ++, но в противном случае она может делать все, что пожелает (см.
std::string
Спор о COW). Некоторые реализации STL особенно плохи.
- предоставить хеши, которые недоступны, если вы не используете TR1
QTL имеет философию, отличную от STL, которая была обобщена Дж. Бланшеттом: «В то время как контейнеры STL оптимизированы для необработанной скорости, классы контейнеров Qt были тщательно разработаны для обеспечения удобства, минимального использования памяти и минимального расширения кода».
Приведенная выше ссылка предоставляет более подробную информацию о реализации QTL и какие оптимизации используются.
QList<double>
на 32-битной архитектуре для использования памяти, чтобы убедиться в этом.QVector
вместо профиляQList
. Есть довольно хорошее объяснение Qt, что QList предназначен для хранения указателей на объекты. Таким образом, каждый двойной элемент создается динамически, и указатель на этот элемент сохраняетсяQList
. QList выполнен в виде «среднего» контейнера между вектором и связанным списком. Он не предназначен для критических случаев с памятью и производительностью.На этот вопрос сложно ответить. Это может действительно сводиться к философскому / субъективному аргументу.
Что, как говорится...
Я рекомендую правило "Когда в Риме ... Делай, как римляне"
Это означает, что если вы находитесь на земле Qt, используйте код Qt'ians. Это не только для удобства чтения / согласованности. Подумайте, что произойдет, если вы сохраните все в контейнере stl, тогда вам придется передать все эти данные в функцию Qt. Вы действительно хотите управлять кучей кода, который копирует вещи в / из контейнеров Qt. Ваш код уже сильно зависит от Qt, так что вы не делаете его более «стандартным» с помощью контейнеров stl. И какой смысл в контейнере, если каждый раз, когда вы хотите использовать его для чего-то полезного, вы должны скопировать его в соответствующий контейнер Qt?
источник
Контейнеры Qt более ограничены, чем контейнеры STL. Несколько примеров того, где STL лучше (все, что я использовал в прошлом):
QList
(на основе указателя) иQValueList
(на основе значения); Qt 3 имелQPtrList
иQValueList
;; Qt 4 теперь имеетQList
, и это совсем не похоже наQPtrList
илиQValueList
).(Т. Е Даже если вы в конечном итоге с помощью контейнеров Qt, используйте STL-совместимый API подмножество
push_back()
, неappend()
,front()
неfirst()
...) , чтобы избежать портирование еще раз прийти Qt 5. В обоих Qt2-> 3 и Qt3-> 4 переходы, изменения в контейнерах Qt были среди тех, которые требуют наибольшего оттока кода.rbegin()
/rend()
, делая обратную итерацию симметричной для прямой итерации. Не все контейнеры Qt имеют их (ассоциативные не имеют), поэтому обратная итерация излишне сложна.insert()
от разных, но совместимых типов итераторов, что делает ихstd::copy()
гораздо реже необходимыми.Allocator
аргумент шаблона, что делает пользовательское управление памятью тривиальным (требуется typedef) по сравнению с Qt (fork ofQLineEdit
required fors/QString/secqstring/
). РЕДАКТИРОВАТЬ 20171220 : Это отрезает Qt от достижений в дизайне распределителя после C ++ 11 и C ++ 17, ср. например , речь Джона Лакоса ( часть 2 ).std::deque
.std::list
естьsplice()
. Всякий раз, когда я используюstd::list
, это потому, что мне нужноsplice()
.std::stack
,std::queue
правильно агрегировать их базовый контейнер, и не наследовать его, какQStack
,QQueue
делают.QSet
это какstd::unordered_set
, а не какstd::set
.QList
это просто странно .Многие из вышеперечисленных проблем могут быть решены довольно легко в Qt , но библиотека контейнеров в Qt, похоже, в данный момент испытывает недостаток в развитии.
РЕДАКТИРОВАТЬ 20150106 : Потратив некоторое время, пытаясь обеспечить поддержку C ++ 11 для контейнерных классов Qt 5, я решил, что это не стоит этой работы. Если вы посмотрите на работу, которая вкладывается в реализацию стандартных библиотек C ++, совершенно ясно, что классы Qt никогда не догонят. Мы выпустили Qt 5.4 сейчас и
QVector
до сих пор не перемещаем элементы в перераспределениях, не имеемemplace_back()
или rvalue-push_back()
... Мы также недавно отклонилиQOptional
шаблон класса, ожидаяstd::optional
вместо этого. Аналогично дляstd::unique_ptr
. Я надеюсь, что эта тенденция продолжается.источник
QList
был эквивалентstd::deque
. Очевидно, я не должен был просто просматривать документацию.QVector
имеетcrbegin
и друзей с Qt 5.6std::reverse_iterator
через сломанныеQHash
/QMap
итераторы, которые при разыменовании возвращаютmapped_type
вместоvalue_type
). Ничего, что не может быть исправлено, но см. Мой РЕДАКТИРОВАТЬ с 2015 года.QVector
использует вint
качестве индекса, ограничивая таким образом 31-битные размеры (даже в 64-битных системах). Более того, он даже не может хранитьINT_MAX
элементы размером более 1 байта. Например, наибольшее значение, которое.size()
я мог иметь вQVector<float>
x86_64 для Linux, составляло 536870907 элементов (2 ²-5), при этом былоstd::vector<float>
успешно выделено 4294967295 элементов (2 ³ ²- 1; больше не пытался из-за нехватки ОЗУ для этого (этот размер уже занимает 16 ГБ) ).Давайте разберем эти утверждения в реальные измеримые явления:
Полегче
Утверждение, сделанное в этом контексте, заключается в том, что итерация в стиле Java несколько «проще», чем в стиле STL, и поэтому Qt проще в использовании благодаря этому дополнительному интерфейсу.
Стиль Java:
Стиль STL:
Стиль итератора Java имеет преимущество в том, что он немного меньше и чище. Проблема в том, что это больше не стиль STL.
Стиль C ++ 11 STL
или
C ++ 11 стиль foreach
И это настолько просто, что нет никакой причины использовать что-либо еще (если только вы не поддерживаете C ++ 11).
Мой любимый, однако, это:
Итак, как мы видим, этот интерфейс не дает нам ничего, кроме дополнительного интерфейса, поверх уже гладкого, обтекаемого и современного интерфейса. Добавление ненужного уровня абстракции поверх уже стабильного и удобного интерфейса? Не моя идея "проще".
Кроме того, интерфейсы Qt foreach и java добавляют накладные расходы; они копируют структуру и обеспечивают ненужный уровень косвенности. Это может показаться не таким уж большим, но зачем добавлять уровень накладных расходов, чтобы обеспечить не намного более простой интерфейс? У Java есть этот интерфейс, потому что у java нет перегрузки операторов; С ++ делает.
безопаснее
Обоснованием, которое дает Qt, является неявная проблема совместного использования, которая не является ни неявной, ни проблемой. Однако это связано с обменом.
Во-первых, это не подразумевается; Вы явно назначаете один вектор другому. Спецификация итераторов STL четко указывает, что итераторы принадлежат контейнеру, поэтому мы четко представили общий контейнер между b и a. Во-вторых, это не проблема; до тех пор, пока все правила спецификации итератора соблюдены, абсолютно ничего не пойдет не так. Единственный раз, когда что-то идет не так, здесь:
Qt определяет это так, как будто это что-то значит, как будто проблема возникает de novo из этого сценария. Это не так. Итератор признан недействительным, и, как и все, к чему можно получить доступ из нескольких непересекающихся областей, именно так он и работает. На самом деле, это легко произойдет с итераторами в стиле Java в Qt, так как он сильно зависит от неявного совместного использования, который является антипаттерном, как описано здесь , и во многих других областях . Особенно странно, что эта «оптимизация» используется в среде, которая все больше и больше движется в направлении многопоточности, но это маркетинг для вас.
Более легкий
Этот немного сложнее. Использование стратегий копирования-при-записи и неявного совместного использования и роста делает очень трудным фактически гарантировать, сколько памяти будет использовать ваш контейнер в любой момент времени. Это не похоже на STL, который дает вам сильные алгоритмические гарантии.
Мы знаем, что минимальная граница потерянного пространства для вектора - это квадратный корень из длины вектора , но, похоже, нет способа реализовать это в Qt; различные «оптимизации», которые они поддерживают, исключают эту очень важную функцию экономии места. STL не требует этой функции (и большинство использует удвоение роста, что является более расточительным), но важно отметить, что вы могли бы по крайней мере реализовать эту функцию, если это будет необходимо.
То же самое относится и к двусвязным спискам, которые могут использовать XOr-ссылки для существенного сокращения используемого пространства. Опять же, это невозможно с Qt из-за требований к росту и COW.
COW действительно может сделать что-то легче, как и Intrusive Containers, такие как поддерживаемые boost , и Qt часто использовал их в более ранних версиях, но они больше не используются так часто, потому что их трудно использовать, они небезопасны и обременительны. на программиста. COW - гораздо менее навязчивое решение, но непривлекательное по причинам, изложенным выше.
Нет никаких причин, по которым вы не могли бы использовать контейнеры STL с той же стоимостью памяти или меньшими, чем у контейнеров Qt, с дополнительным преимуществом фактического знания того, сколько памяти вы будете тратить в любой момент времени. К сожалению, невозможно сравнивать эти два параметра при использовании необработанной памяти, поскольку такие тесты будут показывать совершенно разные результаты в разных случаях использования, что является именно той проблемой, для устранения которой был разработан STL.
В заключение
Избегайте использования контейнеров Qt, когда это возможно, без наложения затрат на копирование, и по возможности используйте итерацию типа STL (возможно, через оболочку или новый синтаксис).
источник
Adding an unnecessary level of abstraction on top of an already stable and usable interface? Not my idea of "easier".
итераторы Qt в стиле Java не были добавлены в C ++ 11; они предшествуют этому. В любом случае, Qtforeach(QString elem, list)
так же прост, как foreach в C ++ 11 или BOOST_FOREACH, и работает с компиляторами, совместимыми с pre-C ++ 11.So, as we can see, this interface gains us nothing except an additional interface, *on top of* an already sleek, streamlined, and modern interface. Adding an unnecessary level of abstraction on top of an already stable and usable interface? Not my idea of "easier".
(выделение мое) Вы сказали это сразу после того, как показали нам версии foreach для C ++ 11 и BOOST, и это звучит так, будто версия Qt построена на основе одной из этих двух, что не так, как AFAICT. Я уверен, что это не то, что вы имели в виду, но вот как это получается. Отсюда и «вводящая в заблуждение информация».It's an additional layer of abstraction (and an unnecessary one) that bloats the interface, which is not easier.
До сих пор неясно, с чем вы сравниваете. C ++ 03 итераторы? Итераторы C ++ 11? BOOST_FOREACH? Все вышеперечисленное?Контейнеры STL:
источник
Контейнеры Qt используют идиому копирования при записи.
источник
std::basic_string
стандарт, в котором были предприняты действия с C ++ 11, чтобы сделать это несовместимым.Одна из основных проблем заключается в том, что API Qt ожидает, что вы предоставите данные в контейнерах Qt, поэтому вы можете просто использовать контейнеры Qt вместо того, чтобы переходить между ними.
Кроме того, если вы уже используете контейнеры Qt, возможно, было бы несколько более оптимальным использовать их исключительно, поскольку вам не нужно будет включать заголовочные файлы STL и, возможно, создавать ссылки в библиотеках STL. Однако, в зависимости от вашего набора инструментов, это может произойти в любом случае. Чисто с точки зрения дизайна, последовательность, как правило, хорошая вещь.
источник
Если данные, с которыми вы работаете, в основном используются для управления пользовательским интерфейсом на основе Qt, то обязательно используйте контейнеры Qt.
Если данные в основном используются внутри приложения, и вы вряд ли сможете портировать их с Qt, то, исключая проблемы с производительностью, используйте контейнеры Qt, поскольку это облегчит работу с битами данных, поступающими в пользовательский интерфейс.
Если данные в основном используются вместе с другими библиотеками, которые знают только о контейнерах STL, используйте контейнеры STL. Если у вас возникла такая ситуация, то вы попали в беду, несмотря ни на что, потому что вы собираетесь много портировать туда и обратно между типами контейнеров, независимо от того, что вы делаете.
источник
Помимо разницы в COW, контейнеры STL гораздо шире поддерживаются на различных платформах. Qt достаточно портативен, если вы ограничиваете свою работу «основными» платформами, но STL доступен и на многих других более неясных платформах (например, DSP от Texas Instruments).
Поскольку STL является стандартным, а не управляемым одной корпорацией, то, как правило, есть больше программистов, которые могут легко читать, понимать и изменять код STL, и больше ресурсов (книг, онлайн-форумов, конференций и т. Д.) Для их поддержки в делать это, чем есть для Qt. Это не значит, что нужно уклоняться от Qt только по этой причине; просто при прочих равных условиях вы должны использовать STL по умолчанию, но, конечно, все редко бывают равными, поэтому вам придется выбирать в своем собственном контексте, который имеет больше смысла.
Что касается ответа AlexKR: производительность STL гарантируется в определенных пределах, но в данной реализации могут использоваться детали, зависящие от платформы, для ускорения их STL. Таким образом, в этом смысле вы можете получить разные результаты на разных платформах, но это никогда не будет медленнее, чем явная гарантия (по модулю ошибок).
источник
Мои пять центов: контейнеры Qt должны работать одинаково на разных платформах. В то время как контейнеры STL зависят от реализации STL. Вы можете получить разные результаты производительности.
РЕДАКТИРОВАТЬ: Я не говорю, что STL "медленнее", но я указываю на эффекты различных деталей реализации.
Пожалуйста, проверьте это , а затем, возможно, это .
И это не настоящая проблема STL. Очевидно, что если у вас есть существенная разница в производительности, то есть проблема в коде, который использует STL.
источник
Я думаю, это зависит от того, как вы используете Qt. Если вы используете его по всему продукту, то, вероятно, имеет смысл использовать контейнеры Qt. Если вы используете его только (например) для части пользовательского интерфейса, может быть лучше использовать стандартные контейнеры C ++.
источник
Я придерживаюсь мнения, что STL является отличным программным обеспечением, однако, если я собираюсь заняться программированием на KDE или Qt, то Qt - это путь. Также это зависит от того, какой компилятор вы используете, с GCC STL работает довольно хорошо, однако, если вам придется использовать, скажем, SUN Studio CC, то STL, скорее всего, принесет вам головную боль из-за компилятора, а не STL как такового. В этом случае, так как компилятор причинит вам боль, просто используйте Qt, чтобы избавить вас от проблем. Просто мои 2 цента ...
источник
В QVector есть (иногда) большое ограничение. Он может выделять только int байты памяти (обратите внимание, что ограничение указывается в байтах, а не в количестве элементов). Это означает, что попытка выделить смежные блоки памяти размером более ~ 2 ГБ с помощью QVector приведет к сбою. Это происходит с Qt 4 и 5. std :: vector не имеет такого ограничения.
источник
Основная причина, по которой я могу использовать STL-контейнеры, заключается в том, что вам нужен собственный распределитель, чтобы повторно использовать память в очень больших контейнерах. Предположим, например, что у вас есть QMap, в котором хранится 1000000 записей (пары ключ / значение). В Qt это означает ровно 1000000 миллионов выделений (
new
вызовов), несмотря ни на что. В STL вы всегда можете создать собственный распределитель, который выделяет всю эту память за один раз и назначает ее каждой записи при заполнении карты.Я советую использовать контейнеры STL при написании критичных для производительности алгоритмов в бизнес-логике, а затем преобразовывать их обратно в контейнеры Qt, когда результаты будут готовы к отображению элементами управления пользовательского интерфейса и формами, если это необходимо.
источник
QMapNode<K,V>
на своихK
,V
чтобы предоставить свои собственныеoperator new
.