- Description: Qt's container family (
QList/QVector/QHash/QMap/QSet/QStack/QQueue), implicit sharing (COW), STL interop, QString and QByteArray, QVariant, and the i18n pipeline (tr + lupdate + Qt Linguist + QTranslator)
- My Notion Note ID: K2A-B3-7
- Created: 2018-03-04
- Updated: 2026-05-18
- License: Reuse is very welcome. Please credit Yu Zhang and link back to the original on yuzhang.io
Table of Contents
1. Sequential Containers
| Container |
Behavior |
Notes |
QList<T> |
Dynamic array. In Qt 6, QList and QVector are the same type (QList is an alias for QVector). |
Default choice for sequences. |
QVector<T> |
Dynamic array. Kept for compatibility. |
Identical to QList in Qt 6. |
QVarLengthArray<T, N> |
Stack-allocated for size ≤ N, heap above. |
Cheap small-N optimization. |
QStack<T> |
LIFO. Adds push/pop/top to QList. |
|
QQueue<T> |
FIFO. Adds enqueue/dequeue/head to QList. |
|
QLinkedList<T> |
Removed in Qt 6 — use std::list if you really need a linked list. |
|
QList<int> nums = {1, 2, 3};
nums.append(4);
nums.prepend(0);
nums.removeAt(1);
int first = nums.first();
int last = nums.last();
qsizetype n = nums.size();
QStack<QString> undo;
undo.push("typed 'a'");
QString last_op = undo.pop();
QQueue<Job> jobs;
jobs.enqueue(j);
Job next = jobs.dequeue();
- The Qt 5-to-6
QList redesign was a real API change. Qt 5 QList<T> was a heap-allocated array of T*-or-T (depending on size); Qt 6 QList<T> is a contiguous T[]. Iterator invalidation rules now match std::vector: any insert/remove can invalidate.
2. Associative Containers
| Container |
Ordered? |
Notes |
QMap<K, V> |
Sorted by K |
Lookup O(log n). Useful when you need ordered iteration. |
QMultiMap<K, V> |
Sorted, allows duplicate keys |
values(k) returns all values for key. |
QHash<K, V> |
Unordered (hash table) |
Lookup O(1) amortized. Default choice unless you need ordered iteration. |
QMultiHash<K, V> |
Unordered, duplicates allowed |
|
QSet<T> |
Unordered set |
Backed by QHash<T, ...>. |
QHash<QString, int> counts;
counts["apples"] = 3;
counts["oranges"] = 5;
counts.insert("kiwi", 2);
if (counts.contains("apples")) {
int n = counts.value("apples", 0);
}
for (auto it = counts.cbegin(); it != counts.cend(); ++it) {
qDebug() << it.key() << "->" << it.value();
}
QSet<int> seen;
seen << 1 << 2 << 3;
seen.contains(2);
QHash requires qHash(const T&) overload (provided for built-in types and most Qt types — QString, QByteArray, QUrl, etc.). Custom keys need a user-defined qHash + operator==.
QHash is unordered AND iteration order is unstable across runs (Qt randomizes the seed to prevent hash-flooding attacks since 5.0). If tests depend on iteration order, sort the keys first.
3. Implicit Sharing (COW)
- Qt's containers,
QString, QByteArray, QPixmap, QImage, and most "value-like" types are implicitly shared: copying them only bumps a reference count; the underlying buffer is duplicated lazily on the first mutating operation.
QList<int> a = makeBigList();
QList<int> b = a;
b.append(99);
- Implications:
- Pass-by-value is cheap, even for huge containers. You can return
QList<T> from functions without worrying.
- You don't need
const QList<T> & for "input parameter you won't modify" — by-value is fine. (Style guides differ; const & is still common because it documents intent.)
- Don't take a
T* to internal storage and expect it to stay valid across a copy. The next mutation detaches and you're holding into a dead buffer.
QList<int> a = {1, 2, 3};
int *p = a.data();
QList<int> b = a;
b[0] = 9;
detach() forces a deep copy now if you want to guarantee uniqueness.
4. Iteration Styles
- Three idioms, all common in Qt code:
QList<QString> names = ...;
for (const QString &n : names) {
use(n);
}
for (auto it = names.cbegin(); it != names.cend(); ++it) {
use(*it);
}
QListIterator<QString> i(names);
while (i.hasNext()) {
use(i.next());
}
QMutableListIterator<QString> mi(names);
while (mi.hasNext()) {
if (mi.next() == "drop") mi.remove();
}
- Prefer the range-based loop. Java-style iterators (
QListIterator, QMutableHashIterator, etc.) date from before C++11 lambdas; they still work but read as old-style.
foreach keyword — Qt's pre-C++11 Q_FOREACH(x, container) macro is discouraged in Qt 6 and can be turned off project-wide with QT_NO_FOREACH. Use range-for.
5. QString
- Unicode (UTF-16 internally), implicitly shared. The everywhere-string of Qt code.
QString s = "hello";
s.append(", world");
int n = s.length();
QString upper = s.toUpper();
QString line = QStringLiteral("user=%1 id=%2").arg(name).arg(id);
int x = QString("42").toInt();
double d = QString("3.14").toDouble();
QString z = QString::number(42);
QString w = QString::number(3.14, 'f', 2);
bool has = s.contains("world");
int idx = s.indexOf(',');
QString first = s.left(5);
QString rest = s.mid(7);
QStringList parts = s.split(", ");
QStringLiteral("text") makes the string a compile-time constant — no runtime UTF-8 → UTF-16 conversion or heap allocation. Use it for any string literal you pass to a QString parameter.
length() counts UTF-16 code units, not user-perceived characters. Emoji and CJK extension B characters are 2 code units. For "glyph count", iterate QStringIterator over grapheme clusters.
6. QByteArray, QStringList, QVariant
QByteArray — sequence of char. Use for raw bytes (file contents, network buffers, hashes). Convert with QString::fromUtf8(bytes) and string.toUtf8().
QFile f("data.bin");
f.open(QIODevice::ReadOnly);
QByteArray bytes = f.readAll();
QByteArray hex = bytes.toHex();
QByteArray b64 = bytes.toBase64();
QStringList — typedef-ish for QList<QString>. Lots of string-aware conveniences: join, filter, replaceInStrings.
QStringList paths = QString("/a/b/c").split('/');
QString rejoined = paths.join('/');
QVariant — type-erased container for any of Qt's "meta-types". Powers Q_PROPERTY, models (QAbstractItemModel::data returns QVariant), settings (QSettings).
QVariant v = 42;
qDebug() << v.typeName();
int n = v.toInt();
v = QString("hello");
QString s = v.toString();
v = QPointF(1, 2);
auto pt = v.value<QPointF>();
- For custom types:
Q_DECLARE_METATYPE(MyClass) + qRegisterMetaType<MyClass>() makes them storable in QVariant and queue-able across threads.
7. STL Interop and "Qt vs STL"
- Qt containers and
std containers are usually interchangeable. Qt provides STL-style iterators (begin, end, cbegin, cend), so they work with std::find, std::sort, range-for.
QList<int> qs = {3, 1, 4, 1, 5};
std::sort(qs.begin(), qs.end());
std::vector<int> v(qs.begin(), qs.end());
QList<int> back(v.begin(), v.end());
- Should you use Qt containers or
std::?
- Mixed Qt code (Qt models, signals/slots with container args, custom types in
QVariant): use Qt containers. Saves marshaling. QHash is also faster than older std::unordered_map on many compilers (less of a difference with newer libcxx/libstdcxx).
- Pure algorithmic code, libraries you'd want portable:
std::vector, std::unordered_map, std::string. Avoid pulling all of Qt into a math library.
- Avoid round-tripping
std::string ↔ QString per call inside a hot path — the UTF-8/UTF-16 conversion isn't free. Pick one and stick with it across the API boundary.
8. Internationalization
8.1 The tr Pipeline
tr("text") is a no-op at runtime if no translator is loaded — returns "text" unchanged. With a .qm loaded, it returns the translation.
class MyForm : public QWidget {
Q_OBJECT
void buildUi() {
title_->setText(tr("Welcome"));
ok_ ->setText(tr("&OK"));
}
};
- The tooling chain:
lupdate myapp.pro (or lupdate -ts i18n/*.ts $(find src -name '*.cpp')) — scans source, writes myapp_en.ts, myapp_zh.ts, etc. Each tr call becomes a <message><source>...</source><translation>...</translation></message> entry.
- Translators open the
.ts file in Qt Linguist and fill in translations.
lrelease myapp.pro compiles .ts → binary .qm files.
- At runtime your app loads the
.qm matching the user's locale.
8.2 QTranslator and Runtime Locale
QApplication app(argc, argv);
QTranslator translator;
const QString locale = QLocale::system().name();
if (translator.load("myapp_" + locale, ":/i18n")) {
app.installTranslator(&translator);
}
MyMainWindow w;
w.show();
return app.exec();
installTranslator calls every existing QObject's changeEvent(QEvent::LanguageChange). To support live language switching at runtime, override changeEvent and re-call all your setText(tr(...)) lines.
- For Qt's own dialog text (Save / Cancel / etc.) load Qt's bundled translation too:
QTranslator qt;
qt.load("qtbase_" + locale, QLibraryInfo::path(QLibraryInfo::TranslationsPath));
app.installTranslator(&qt);
8.3 Q_DECLARE_TR_FUNCTIONS
- A class that's not a
QObject doesn't have tr. Add it manually:
class ErrorTable {
Q_DECLARE_TR_FUNCTIONS(ErrorTable)
public:
QString message(int code) const {
switch (code) {
case 1: return tr("File not found");
case 2: return tr("Permission denied");
}
return tr("Unknown error");
}
};
- The macro injects a static
tr whose translation context is the class name. lupdate recognizes it the same as QObject::tr.
8.4 Plurals and Disambiguation
tr accepts an optional second arg for disambiguation (when the same English word has different translations in different contexts), and a third for plural count:
tr("Open", "menu item");
tr("Open", "verb in toolbar");
QString s = tr("%n file(s) selected", "", count);
- The plural form lets translators handle languages with multiple plural categories (Russian, Polish, Arabic). Qt Linguist shows separate fields for "singular", "dual", "few", "many", etc., per the target language's rules.
8.5 QLocale
- Number/date/time/currency formatting per region.
QLocale us(QLocale::English, QLocale::UnitedStates);
QString s = us.toString(1234567.89, 'f', 2);
QString d = us.toString(QDate::currentDate(), QLocale::ShortFormat);
QLocale system = QLocale::system();
qDebug() << system.name();
qDebug() << system.language();
QLocale::system() reflects the user's OS locale at the time of the call. Cache it at app start unless you support live language switching.
8.6 Text Codecs (Qt 5 vs Qt 6)
- Qt 5 required
QTextCodec for non-UTF-8 byte ↔ string conversions:
#include <QTextCodec>
QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF-8"));
QString s = QTextCodec::codecForName("Shift-JIS")->toUnicode(bytes);
- Qt 6 removed
QTextCodec from QtCore. UTF-8 is assumed for the locale, and QString's constructors expect UTF-8 bytes by default. For legacy encodings, use QStringConverter / QStringDecoder / QStringEncoder:
QStringDecoder fromShiftJis(QStringConverter::System);
QStringDecoder fromGbk("GBK");
QString s = fromGbk(bytes);
QStringEncoder toUtf16 = QStringEncoder("UTF-16LE");
QByteArray out = toUtf16(s);
QStringConverter::Encoding enum lists the built-in encodings: UTF-8, UTF-16, UTF-16LE/BE, UTF-32 variants, Latin-1, System. Anything else needs the Qt5Compat module (Qt6::Core5Compat::QTextCodec) — Qt 6 keeps QTextCodec available as a compatibility shim.
9. References