C 语言

关注公众号 jb51net

关闭
首页 > 软件编程 > C 语言 > Qt foreach

Qt中foreach的实现示例

作者:上去我就QWER

本文主要介绍了Qt中foreach的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

在 Qt 中,foreach 是一个 Qt 扩展的关键字(宏定义),用于遍历容器类元素,语法简洁、使用方便,底层基于容器的迭代器实现,但屏蔽了迭代器的复杂操作,让遍历逻辑更直观。它支持 Qt 容器(如 QListQVectorQMapQHash 等)和 STL 容器(如 std::vectorstd::list 等),是 Qt 开发中遍历容器的常用方式。

一、核心本质:宏定义而非原生 C++ 语法

foreach 并非 C++ 标准关键字,而是 Qt 通过 #define 定义的宏,其底层映射到容器的迭代器遍历逻辑。Qt 5 及以上版本默认启用该宏,若需禁用(如避免与其他库冲突),可在项目文件(.pro)中添加:

DEFINES += QT_NO_FOREACH

二、基本语法

1. 遍历“值类型容器”(如 QList、QVector、std::vector)

语法:

foreach (const 元素类型 &变量名, 容器对象) {
    // 遍历逻辑(变量名表示当前元素)
}
// 或(非 const,允许修改元素,仅适用于可修改的容器)
foreach (元素类型 &变量名, 容器对象) {
    // 修改元素的逻辑
}

示例:遍历 QList

QList<QString> fruits = {"苹果", "香蕉", "橙子"};

// 只读遍历(推荐用 const &,避免拷贝)
foreach (const QString &fruit, fruits) {
    qDebug() << fruit; // 输出:"苹果" "香蕉" "橙子"
}

// 可修改遍历(需用非 const 引用,容器必须是可修改的)
foreach (QString &fruit, fruits) {
    fruit = "[水果]" + fruit; // 修改元素
}
qDebug() << fruits; // 输出:["[水果]苹果", "[水果]香蕉", "[水果]橙子"]

2. 遍历“键值对容器”(如 QMap、QHash、std::map)

Qt 的键值对容器(QMap<K, V>QHash<K, V>)遍历后,foreach 的“元素类型”是 QPair<K, V>(或 std::pair<K, V> 对于 STL 容器),通过 first 访问键、second 访问值。

语法:

foreach (const QPair<键类型, 值类型> &pair, 键值对容器) {
    qDebug() << "键:" << pair.first << ",值:" << pair.second;
}

示例:遍历 QMap<int, QString>

QMap<int, QString> studentMap;
studentMap.insert(101, "张三");
studentMap.insert(102, "李四");
studentMap.insert(103, "王五");

// 遍历键值对
foreach (const QPair<int, QString> &pair, studentMap) {
    qDebug() << "学号:" << pair.first << ",姓名:" << pair.second;
}
// 输出(QMap 按键排序):
// 学号: 101 ,姓名: "张三"
// 学号: 102 ,姓名: "李四"
// 学号: 103 ,姓名: "王五"

3. 简化写法:使用auto(Qt 5.7+ 支持 C++11 及以上)

若不想显式写元素类型,可结合 auto 关键字(需开启 C++11 支持,.pro 中添加 CONFIG += c++11):

QList<int> nums = {1, 2, 3, 4};
foreach (const auto &num, nums) {
    qDebug() << num; // 自动推导类型为 int
}

QHash<QString, int> scoreHash = {{"数学", 90}, {"语文", 85}};
foreach (const auto &pair, scoreHash) {
    qDebug() << pair.first << ":" << pair.second; // 自动推导为 QPair<QString, int>
}

三、关键特性与注意事项

1. 遍历的是“容器的拷贝”(重要!)

foreach 遍历的是容器的 临时拷贝,而非容器本身。这意味着:

反例:遍历中修改容器无效

QList<int> nums = {1, 2, 3};
foreach (const int &num, nums) {
    nums.append(num * 2); // 向原容器添加元素,但遍历的是拷贝,不会遍历到新元素
}
qDebug() << nums; // 输出:[1,2,3,2,4,6](原容器被修改,但遍历未包含新元素)

2. 支持“空容器”和“单元素容器”

foreach 会自动处理空容器(不执行循环体),无需手动判断容器是否为空:

QVector<QString> emptyVec;
foreach (const auto &str, emptyVec) {
    qDebug() << str; // 不会执行
}

3. 与 Qt 容器的兼容性

foreach 完美支持所有 Qt 容器类:

4. 与 STL 容器的兼容性

Qt 5.0+ 支持用 foreach 遍历 STL 容器(如 std::vectorstd::liststd::map),语法与 Qt 容器一致:

#include <vector>
#include <list>

std::vector<int> stlVec = {10, 20, 30};
foreach (const auto &val, stlVec) {
    qDebug() << val; // 输出:10 20 30
}

std::map<std::string, int> stlMap = {{"a", 1}, {"b", 2}};
foreach (const auto &pair, stlMap) {
    qDebug() << QString::fromStdString(pair.first) << ":" << pair.second; // 输出:"a":1 "b":2
}

5. 避免使用“容器的引用”作为遍历对象

若误将容器的引用传给 foreach,会导致遍历的是“引用的拷贝”,仍无法修改原容器,且语法冗余,不推荐:

QList<int> nums = {1,2,3};
// 不推荐:&nums 是引用,但 foreach 仍会拷贝引用指向的容器
foreach (const int &num, nums) { 
    // ...
}

6. 元素为“指针/智能指针”时的注意事项

若容器存储的是指针(如 QList<QObject*>),foreach 遍历的是指针的拷贝(而非对象的拷贝),此时修改指针指向的对象是有效的,但修改指针本身(如赋值为 nullptr)无效:

QList<QObject*> objList;
objList.append(new QObject);
objList.append(new QObject);

foreach (QObject *obj, objList) {
    obj->setObjectName("test"); // 有效:修改指针指向的对象的属性
    obj = nullptr; // 无效:仅修改拷贝的指针,原容器中的指针不变
}

foreach (QObject *obj, objList) {
    qDebug() << obj->objectName(); // 输出:"test" "test"
    delete obj;
}

四、foreach与迭代器、范围 for 的对比

特性foreach(Qt 宏)迭代器(QList::iterator)范围 for(C++11+)
语法简洁性最高(无需手动控制迭代)中等(需初始化迭代器)高(原生语法,简洁)
遍历对象容器拷贝容器本身容器本身(可通过引用控制)
遍历中修改容器无效(修改拷贝)有效(需注意迭代器失效)有效(需注意容器类型)
性能(大对象容器)较低(拷贝开销)较高(无拷贝)较高(无拷贝)
兼容性Qt 容器 + STL 容器Qt 容器 + STL 容器Qt 容器 + STL 容器(C++11+)
适用场景简单遍历、无需修改容器复杂遍历(如插入/删除)现代 C++ 开发、追求原生语法

推荐选择:

五、常见错误与解决方案

错误 1:遍历中修改容器,期望影响遍历结果

原因foreach 遍历的是拷贝,修改原容器不影响遍历。
解决方案:改用迭代器或范围 for:

QList<int> nums = {1,2,3};
// 用迭代器遍历并修改
for (auto it = nums.begin(); it != nums.end(); ++it) {
    *it *= 2; // 直接修改原容器元素
}
qDebug() << nums; // 输出:[2,4,6]

错误 2:遍历大对象容器时性能低下

原因:拷贝大对象带来开销。
解决方案:用 const_iterator 或范围 for(无拷贝):

// 大对象容器(如 QByteArray)
QList<QByteArray> bigList;
// ... 填充大量 QByteArray ...

// 用范围 for 遍历(无拷贝)
for (const auto &ba : bigList) {
    qDebug() << ba.size();
}

错误 3:混淆键值对容器的元素类型

原因QMap/QHash 的元素是 QPair,而非单独的键或值。
解决方案:通过 pair.first(键)和 pair.second(值)访问:

QHash<QString, int> scoreHash = {{"英语", 95}};
// 错误:元素类型是 QPair,不是 int
// foreach (const int &score, scoreHash) { ... }

// 正确:
foreach (const auto &pair, scoreHash) {
    qDebug() << pair.first << ":" << pair.second; // 英语:95
}

六、总结

foreach 是 Qt 提供的简洁遍历工具,核心优势是语法简单、无需关注迭代器细节,适合大多数“只读、不修改容器”的场景。其底层基于容器拷贝,因此需注意:

  1. 遍历中修改容器无效;
  2. 大对象容器慎用(拷贝开销);
  3. 键值对容器需通过 QPair 访问键和值。

若需复杂遍历(如插入/删除元素)或追求更高性能,建议使用迭代器或 C++11 范围 for 循环。

到此这篇关于Qt中foreach的实现示例的文章就介绍到这了,更多相关Qt foreach内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文