Давайте разберем эти утверждения в реальные измеримые явления:
- Легче: контейнеры Qt используют меньше памяти, чем контейнеры STL
- Безопаснее: у контейнеров Qt меньше возможностей для неправильного использования
- Проще: контейнеры Qt представляют меньшую интеллектуальную нагрузку
Полегче
Утверждение, сделанное в этом контексте, заключается в том, что итерация в стиле Java несколько «проще», чем в стиле STL, и поэтому Qt проще в использовании благодаря этому дополнительному интерфейсу.
Стиль Java:
QListIterator<QString> i(list);
while (i.hasNext())
qDebug() << i.next();
Стиль STL:
QList<QString>::iterator i;
for (i = list.begin(); i != list.end(); ++i)
qDebug << *i;
Стиль итератора Java имеет преимущество в том, что он немного меньше и чище. Проблема в том, что это больше не стиль STL.
Стиль C ++ 11 STL
for( auto i = list.begin(); i != list.end(); ++i)
qDebug << *i;
или
C ++ 11 стиль foreach
for (QString i : list)
qDebug << i;
И это настолько просто, что нет никакой причины использовать что-либо еще (если только вы не поддерживаете C ++ 11).
Мой любимый, однако, это:
BOOST_FOREACH(QString i, list)
{
qDebug << i;
}
Итак, как мы видим, этот интерфейс не дает нам ничего, кроме дополнительного интерфейса, поверх уже гладкого, обтекаемого и современного интерфейса. Добавление ненужного уровня абстракции поверх уже стабильного и удобного интерфейса? Не моя идея "проще".
Кроме того, интерфейсы Qt foreach и java добавляют накладные расходы; они копируют структуру и обеспечивают ненужный уровень косвенности. Это может показаться не таким уж большим, но зачем добавлять уровень накладных расходов, чтобы обеспечить не намного более простой интерфейс? У Java есть этот интерфейс, потому что у java нет перегрузки операторов; С ++ делает.
безопаснее
Обоснованием, которое дает Qt, является неявная проблема совместного использования, которая не является ни неявной, ни проблемой. Однако это связано с обменом.
QVector<int> a, b;
a.resize(100000); // make a big vector filled with 0.
QVector<int>::iterator i = a.begin();
// WRONG way of using the iterator i:
b = a;
/*
Now we should be careful with iterator i since it will point to shared data
If we do *i = 4 then we would change the shared instance (both vectors)
The behavior differs from STL containers. Avoid doing such things in Qt.
*/
Во-первых, это не подразумевается; Вы явно назначаете один вектор другому. Спецификация итераторов STL четко указывает, что итераторы принадлежат контейнеру, поэтому мы четко представили общий контейнер между b и a. Во-вторых, это не проблема; до тех пор, пока все правила спецификации итератора соблюдены, абсолютно ничего не пойдет не так. Единственный раз, когда что-то идет не так, здесь:
b.clear(); // Now the iterator i is completely invalid.
Qt определяет это так, как будто это что-то значит, как будто проблема возникает de novo из этого сценария. Это не так. Итератор признан недействительным, и, как и все, к чему можно получить доступ из нескольких непересекающихся областей, именно так он и работает. На самом деле, это легко произойдет с итераторами в стиле Java в Qt, так как он сильно зависит от неявного совместного использования, который является антипаттерном, как описано здесь , и во многих других областях . Особенно странно, что эта «оптимизация» используется в среде, которая все больше и больше движется в направлении многопоточности, но это маркетинг для вас.
Более легкий
Этот немного сложнее. Использование стратегий копирования-при-записи и неявного совместного использования и роста делает очень трудным фактически гарантировать, сколько памяти будет использовать ваш контейнер в любой момент времени. Это не похоже на STL, который дает вам сильные алгоритмические гарантии.
Мы знаем, что минимальная граница потерянного пространства для вектора - это квадратный корень из длины вектора , но, похоже, нет способа реализовать это в Qt; различные «оптимизации», которые они поддерживают, исключают эту очень важную функцию экономии места. STL не требует этой функции (и большинство использует удвоение роста, что является более расточительным), но важно отметить, что вы могли бы по крайней мере реализовать эту функцию, если это будет необходимо.
То же самое относится и к двусвязным спискам, которые могут использовать XOr-ссылки для существенного сокращения используемого пространства. Опять же, это невозможно с Qt из-за требований к росту и COW.
COW действительно может сделать что-то легче, как и Intrusive Containers, такие как поддерживаемые boost , и Qt часто использовал их в более ранних версиях, но они больше не используются так часто, потому что их трудно использовать, они небезопасны и обременительны. на программиста. COW - гораздо менее навязчивое решение, но непривлекательное по причинам, изложенным выше.
Нет никаких причин, по которым вы не могли бы использовать контейнеры STL с той же стоимостью памяти или меньшими, чем у контейнеров Qt, с дополнительным преимуществом фактического знания того, сколько памяти вы будете тратить в любой момент времени. К сожалению, невозможно сравнивать эти два параметра при использовании необработанной памяти, поскольку такие тесты будут показывать совершенно разные результаты в разных случаях использования, что является именно той проблемой, для устранения которой был разработан STL.
В заключение
Избегайте использования контейнеров Qt, когда это возможно, без наложения затрат на копирование, и по возможности используйте итерацию типа STL (возможно, через оболочку или новый синтаксис).