Qt中foreach的實現(xiàn)示例
在 Qt 中,foreach 是一個 Qt 擴展的關(guān)鍵字(宏定義),用于遍歷容器類元素,語法簡潔、使用方便,底層基于容器的迭代器實現(xiàn),但屏蔽了迭代器的復(fù)雜操作,讓遍歷邏輯更直觀。它支持 Qt 容器(如 QList、QVector、QMap、QHash 等)和 STL 容器(如 std::vector、std::list 等),是 Qt 開發(fā)中遍歷容器的常用方式。
一、核心本質(zhì):宏定義而非原生 C++ 語法
foreach 并非 C++ 標準關(guān)鍵字,而是 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() << "學(xué)號:" << pair.first << ",姓名:" << pair.second;
}
// 輸出(QMap 按鍵排序):
// 學(xué)號: 101 ,姓名: "張三"
// 學(xué)號: 102 ,姓名: "李四"
// 學(xué)號: 103 ,姓名: "王五"
3. 簡化寫法:使用auto(Qt 5.7+ 支持 C++11 及以上)
若不想顯式寫元素類型,可結(jié)合 auto 關(guān)鍵字(需開啟 C++11 支持,.pro 中添加 CONFIG += c++11):
QList<int> nums = {1, 2, 3, 4};
foreach (const auto &num, nums) {
qDebug() << num; // 自動推導(dǎo)類型為 int
}
QHash<QString, int> scoreHash = {{"數(shù)學(xué)", 90}, {"語文", 85}};
foreach (const auto &pair, scoreHash) {
qDebug() << pair.first << ":" << pair.second; // 自動推導(dǎo)為 QPair<QString, int>
}
三、關(guān)鍵特性與注意事項
1. 遍歷的是“容器的拷貝”(重要?。?/h3>
foreach 遍歷的是容器的 臨時拷貝,而非容器本身。這意味著:
- 遍歷過程中修改容器(如添加/刪除元素),不會影響當前遍歷的結(jié)果(因為遍歷的是拷貝);
- 若容器元素是“大對象”(如
QByteArray、自定義結(jié)構(gòu)體),拷貝會帶來性能開銷,此時推薦用 迭代器 或 Qt 5.10+ 提供的Q_FOREACH替代(本質(zhì)相同,但可通過QT_USE_FOREACH_COPY控制是否拷貝,默認仍拷貝)。
反例:遍歷中修改容器無效
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 會自動處理空容器(不執(zhí)行循環(huán)體),無需手動判斷容器是否為空:
QVector<QString> emptyVec;
foreach (const auto &str, emptyVec) {
qDebug() << str; // 不會執(zhí)行
}
3. 與 Qt 容器的兼容性
foreach 完美支持所有 Qt 容器類:
- 順序容器:QList、QVector、QByteArray、QStringList、QLinkedList;
- 關(guān)聯(lián)容器:QMap、QMultiMap、QHash、QMultiHash;
- 其他:QSet、QStack、QQueue(本質(zhì)是順序容器的封裝)。
4. 與 STL 容器的兼容性
Qt 5.0+ 支持用 foreach 遍歷 STL 容器(如 std::vector、std::list、std::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,會導(dǎo)致遍歷的是“引用的拷貝”,仍無法修改原容器,且語法冗余,不推薦:
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+) |
| 適用場景 | 簡單遍歷、無需修改容器 | 復(fù)雜遍歷(如插入/刪除) | 現(xiàn)代 C++ 開發(fā)、追求原生語法 |
推薦選擇:
- 簡單只讀遍歷(Qt 容器):優(yōu)先用
foreach(語法簡潔); - 遍歷中需修改容器/大對象容器:用迭代器或范圍 for;
- 現(xiàn)代 C++ 項目(C++11+):推薦范圍 for(原生支持,無 Qt 依賴)。
五、常見錯誤與解決方案
錯誤 1:遍歷中修改容器,期望影響遍歷結(jié)果
原因: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
}
六、總結(jié)
foreach 是 Qt 提供的簡潔遍歷工具,核心優(yōu)勢是語法簡單、無需關(guān)注迭代器細節(jié),適合大多數(shù)“只讀、不修改容器”的場景。其底層基于容器拷貝,因此需注意:
- 遍歷中修改容器無效;
- 大對象容器慎用(拷貝開銷);
- 鍵值對容器需通過
QPair訪問鍵和值。
若需復(fù)雜遍歷(如插入/刪除元素)或追求更高性能,建議使用迭代器或 C++11 范圍 for 循環(huán)。
到此這篇關(guān)于Qt中foreach的實現(xiàn)示例的文章就介紹到這了,更多相關(guān)Qt foreach內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C++ STL關(guān)聯(lián)式容器自定義排序規(guī)則的2種方法
這篇文章主要介紹了C++ STL關(guān)聯(lián)式容器自定義排序規(guī)則的2種方法,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03
C++數(shù)據(jù)結(jié)構(gòu)哈希表詳解
C++標準庫中使用的unordered_map底層實現(xiàn)是哈希表,下面這篇文章主要給大家介紹了關(guān)于C++中使用哈希表(unordered_map)的一些常用操作方法,需要的朋友可以參考下2022-07-07

