Розбимо ці твердження на фактичні вимірювані явища:
- Запальничка: контейнери 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 стиль передбачення
for (QString i : list)
qDebug << i;
Що настільки різко просто, що немає жодного приводу використовувати будь-що інше (якщо ви не підтримуєте C ++ 11).
Мій улюблений, однак, такий:
BOOST_FOREACH(QString i, list)
{
qDebug << i;
}
Отже, як ми бачимо, цей інтерфейс не отримує від нас нічого, крім додаткового інтерфейсу, окрім уже гладкого, обтічного та сучасного інтерфейсу. Додавання зайвого рівня абстракції поверх уже стабільного та зручного інтерфейсу? Не моя думка про "легше".
Також інтерфейси Qt foreach та java додають накладні витрати; вони копіюють структуру і забезпечують непотрібний рівень непрямості. Це може здатися не дуже схожим, але навіщо додавати шар накладних витрат, щоб забезпечити не такий вже й простіший інтерфейс? У Java є цей інтерфейс, оскільки java не має перевантаження оператора; C ++ робить.
Безпечніше
Обґрунтуванням, яке дає 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 дійсно може зробити щось легше, але це може зробити нав'язливі контейнери, такі як підтримка boost , і Qt використовували їх часто в попередніх версіях, але вони більше не використовуються, оскільки вони важкі у використанні, небезпечні та накладають тягар. на програміста. COW - набагато менш нав'язливе рішення, але непривабливе з причин, викладених вище.
Немає жодної причини, чому ви не могли б використовувати контейнери STL з такою самою вартістю пам'яті або меншою, ніж контейнери Qt, з додатковою перевагою фактично знати, скільки пам’яті ви будете витрачати в будь-який момент часу. На жаль, неможливо порівняти ці два у використанні необробленої пам’яті, оскільки такі орієнтири показали б диво різні результати в різних випадках використання, що є саме тим видом проблеми, який було призначено для виправлення STL.
У висновку
Уникайте використання контейнерів Qt, коли це можливо, без накладення вартості копіювання, і використовуйте ітерацію типу STL (можливо, за допомогою обгортки або нового синтаксису), коли це можливо.