Why Qt Containers?
Qt provides its own container library alongside the C++ STL. Qt containers integrate with Qt’s data model, are implicitly shared (copy-on-write), and work seamlessly with Qt’s range-based for and algorithms.
Qt Container STL Equivalent Qt Advantage
───────────── ────────────── ──────────────────────────
QList<T> std::vector<T> Implicit sharing, Qt API
QMap<K,V> std::map<K,V> Implicit sharing
QHash<K,V> std::unordered_map Faster lookup
QSet<T> std::unordered_set
QString std::wstring Unicode, rich API, QML
QByteArray std::vector<char> Base64, hex, serial data
QList — The Go-To Sequential Container
#include <QList>
QList<int> sensors = {10, 20, 30, 40, 50};
// Append / prepend
sensors.append(60);
sensors.prepend(5);
// Access
qDebug() << sensors.first(); // 5
qDebug() << sensors.last(); // 60
qDebug() << sensors[2]; // 20
// Search
int idx = sensors.indexOf(30); // 3
bool has = sensors.contains(99); // false
// Remove
sensors.removeAt(0); // remove index 0
sensors.removeAll(20); // remove all 20s
sensors.removeFirst();
sensors.removeLast();
// Iterate
for (int v : sensors)
qDebug() << v;
// Filter with STL algorithms
auto above30 = std::copy_if(sensors.begin(), sensors.end(),
QList<int>{}.begin(), [](int v){ return v > 30; });
QVector — Contiguous Memory (Qt 5 vs Qt 6)
In Qt 6,
QList<T>is the same asQVector<T>— contiguous storage by default.
QVector<float> readings(100, 0.0f); // 100 zeros
readings[42] = 3.14f;
// Efficient bulk operations
readings.reserve(1000); // pre-allocate
readings.resize(500); // resize to 500 elements
readings.squeeze(); // free excess capacity
QMap — Ordered Key-Value Store
#include <QMap>
QMap<QString, float> sensorValues;
sensorValues["temperature"] = 36.5f;
sensorValues["humidity"] = 60.2f;
sensorValues["pressure"] = 1013.0f;
// Access
float t = sensorValues.value("temperature", -1.0f); // default -1
// Iterate (sorted by key)
for (auto it = sensorValues.begin(); it != sensorValues.end(); ++it)
qDebug() << it.key() << "=" << it.value();
// Range-based
for (const auto &[key, val] : sensorValues.asKeyValueRange())
qDebug() << key << ":" << val;
// Multi-value map
QMultiMap<QString, int> alerts;
alerts.insert("overheat", 80);
alerts.insert("overheat", 90); // two entries for same key
QHash — Fast Unordered Key-Value
#include <QHash>
QHash<QString, QObject*> devices;
devices["uart-0"] = new UartDevice(this);
devices["spi-1"] = new SpiDevice(this);
// O(1) lookup
QObject *dev = devices.value("uart-0", nullptr);
// Check
if (devices.contains("can-0"))
qDebug() << "CAN device found";
// Remove
devices.remove("spi-1");
// All keys / values
QStringList keys = devices.keys();
QSet — Unique Elements
#include <QSet>
QSet<QString> activeDevices;
activeDevices.insert("uart-0");
activeDevices.insert("spi-1");
activeDevices.insert("uart-0"); // ignored — already present
qDebug() << activeDevices.size(); // 2
// Set operations
QSet<QString> all = {"uart-0", "spi-1", "i2c-2"};
QSet<QString> alive = {"uart-0", "i2c-2"};
QSet<QString> dead = all - alive; // {"spi-1"}
QSet<QString> union_ = all | alive; // all
QSet<QString> inter = all & alive; // {"uart-0","i2c-2"}
QString — Unicode Text
#include <QString>
QString name = "Sensor-001";
// Build strings
QString msg = QString("Device %1 at %2°C").arg(name).arg(36.5, 0, 'f', 1);
QString hex = QString("0x%1").arg(0xFF, 4, 16, QChar('0')); // "0x00FF"
// Query
qDebug() << name.length(); // 10
qDebug() << name.toUpper(); // "SENSOR-001"
qDebug() << name.contains("001"); // true
qDebug() << name.startsWith("S"); // true
// Split / join
QStringList parts = "a,b,c".split(","); // ["a","b","c"]
QString joined = parts.join(" | "); // "a | b | c"
// Number conversions
int i = QString("42").toInt();
float f = QString("3.14").toFloat();
QString s = QString::number(3.14159, 'f', 2); // "3.14"
// Replace / trim
QString raw = " hello world ";
qDebug() << raw.trimmed(); // "hello world"
qDebug() << raw.simplified(); // "hello world"
qDebug() << raw.replace("world", "Qt"); // " hello Qt "
QByteArray — Raw Binary / Serial Data
#include <QByteArray>
// Build from literal
QByteArray packet = "\x02\x01\x00\xFF\x03";
// Hex representation
qDebug() << packet.toHex(' '); // "02 01 00 ff 03"
// From/to hex
QByteArray decoded = QByteArray::fromHex("deadbeef");
qDebug() << decoded.toHex(); // "deadbeef"
// Base64
QByteArray data = "Hello Qt";
QByteArray b64 = data.toBase64();
QByteArray back = QByteArray::fromBase64(b64);
// Append / slice
QByteArray frame;
frame.append('\x02'); // STX
frame.append("payload");
frame.append('\x03'); // ETX
QByteArray payload = frame.mid(1, frame.size() - 2); // strip STX/ETX
// Search
int pos = frame.indexOf('\x03');
Visual: Container Selection Guide
Need ordered key-value?
YES → QMap<K,V>
NO → Need fast O(1) lookup?
YES → QHash<K,V>
NO → Need unique elements?
YES → QSet<T>
NO → Sequential list?
YES → QList<T> (or QVector<T>)
NO → Queue/Stack?
YES → QQueue / QStack
Lab 1 — Device Registry with QHash
struct DeviceInfo {
QString name;
QString type;
float lastReading = 0.0f;
bool online = false;
};
class DeviceRegistry {
QHash<QString, DeviceInfo> m_devices;
public:
void add(const QString &id, const QString &name, const QString &type) {
m_devices[id] = {name, type, 0.0f, true};
}
void update(const QString &id, float value) {
if (m_devices.contains(id))
m_devices[id].lastReading = value;
}
void setOnline(const QString &id, bool online) {
if (m_devices.contains(id))
m_devices[id].online = online;
}
QStringList onlineDevices() const {
QStringList result;
for (auto it = m_devices.begin(); it != m_devices.end(); ++it)
if (it->online) result << it.key();
return result;
}
void printAll() const {
for (const auto &[id, info] : m_devices.asKeyValueRange()) {
qDebug() << id
<< info.name
<< (info.online ? "ONLINE" : "OFFLINE")
<< info.lastReading;
}
}
};
// main
DeviceRegistry reg;
reg.add("uart-0", "UART Bridge", "serial");
reg.add("spi-1", "SPI Flash", "storage");
reg.add("i2c-2", "Temp Sensor", "sensor");
reg.update("i2c-2", 36.5f);
reg.setOnline("spi-1", false);
qDebug() << "Online:" << reg.onlineDevices();
reg.printAll();
Lab 2 — Packet Builder with QByteArray
class PacketBuilder {
public:
// Frame: [STX][LEN][CMD][DATA...][CRC][ETX]
static QByteArray build(quint8 cmd, const QByteArray &data) {
QByteArray frame;
frame.append('\x02'); // STX
frame.append(static_cast<char>(data.size() + 1)); // LEN
frame.append(static_cast<char>(cmd)); // CMD
frame.append(data); // DATA
frame.append(crc8(frame)); // CRC
frame.append('\x03'); // ETX
return frame;
}
static bool parse(const QByteArray &raw,
quint8 &cmd, QByteArray &data)
{
if (raw.size() < 5) return false;
if (raw.front() != '\x02') return false;
if (raw.back() != '\x03') return false;
int len = static_cast<quint8>(raw[1]);
cmd = static_cast<quint8>(raw[2]);
data = raw.mid(3, len - 1);
return true;
}
private:
static char crc8(const QByteArray &data) {
quint8 crc = 0;
for (quint8 b : data) crc ^= b;
return static_cast<char>(crc);
}
};
// Usage
QByteArray payload = QByteArray::fromHex("0102030405");
QByteArray frame = PacketBuilder::build(0xA1, payload);
qDebug() << "Frame:" << frame.toHex(' ');
quint8 cmd; QByteArray parsed;
if (PacketBuilder::parse(frame, cmd, parsed))
qDebug() << "CMD:" << Qt::hex << cmd << "DATA:" << parsed.toHex();
Interview Questions
Q1: What is implicit sharing (copy-on-write) in Qt containers?
When you copy a Qt container, both copies share the same internal data. A deep copy only happens when one of them is modified (write). This makes passing containers by value cheap — like passing a pointer — until a write triggers a real copy.
Q2: When should you use QHash over QMap?
Use
QHashwhen you need O(1) average lookup and don’t need keys in sorted order. UseQMapwhen you need iteration in sorted key order or range queries.QHashis generally 2-3x faster for lookups.
Q3: What is the difference between QList::at() and QList::operator[]?
at()is read-only and asserts bounds in debug mode — safe and fast.operator[]returns a non-const reference and may detach the implicit share for write access. Useat()for read-only access.
Q4: How do you safely convert between QString and std::string?
QString::toStdString()→ UTF-8std::string.QString::fromStdString(s)← UTF-8. For embedded: prefertoLatin1()/toUtf8()to get aQByteArrayfor transmission.
Q5: Why is QStringList common in Qt code?
QStringListisQList<QString>with extra convenience methods:join(),filter(),replaceInStrings(),sort(). It integrates withsplit(),QDir,QProcess, and many Qt APIs.
Application: Config File Parser
class ConfigParser {
QMap<QString, QMap<QString, QString>> m_sections;
public:
bool load(const QString &path) {
QFile file(path);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
return false;
QString currentSection = "global";
QTextStream in(&file);
while (!in.atEnd()) {
QString line = in.readLine().trimmed();
if (line.isEmpty() || line.startsWith('#')) continue;
if (line.startsWith('[') && line.endsWith(']')) {
currentSection = line.mid(1, line.length() - 2);
continue;
}
int eq = line.indexOf('=');
if (eq > 0) {
QString key = line.left(eq).trimmed();
QString val = line.mid(eq + 1).trimmed();
m_sections[currentSection][key] = val;
}
}
return true;
}
QString value(const QString §ion, const QString &key,
const QString &def = {}) const {
return m_sections.value(section).value(key, def);
}
QStringList sections() const { return m_sections.keys(); }
void print() const {
for (const auto &[sec, kvs] : m_sections.asKeyValueRange()) {
qDebug() << "[" + sec + "]";
for (const auto &[k, v] : kvs.asKeyValueRange())
qDebug() << " " << k << "=" << v;
}
}
};
// Config file: device.ini
// [uart]
// port=/dev/ttyUSB0
// baud=115200
// [i2c]
// bus=1
// address=0x48
ConfigParser cfg;
if (cfg.load("device.ini")) {
QString port = cfg.value("uart", "port", "/dev/ttyS0");
int baud = cfg.value("uart", "baud", "9600").toInt();
qDebug() << "UART:" << port << "@" << baud;
}
References
| Resource | Link |
|---|---|
| Qt Docs: Container Classes | https://doc.qt.io/qt-6/containers.html |
| Qt Docs: QString | https://doc.qt.io/qt-6/qstring.html |
| Qt Docs: QByteArray | https://doc.qt.io/qt-6/qbytearray.html |
| Qt Docs: QHash | https://doc.qt.io/qt-6/qhash.html |
Next tutorial → File I/O & Resources