注意:復現代碼時,確保 VS2022 使用 C++17/20 標準以支持現代特性。
遍歷聚合對象的統一方式
1. 模式定義與用途
核心思想
- ?迭代器模式:提供一種方法順序訪問聚合對象的元素,而無需暴露其內部表示。
- 關鍵用途:
1.?統一遍歷接口:為不同數據結構(如數組、鏈表、樹)提供一致的遍歷方式。
?2.支持多種遍歷策略:前向、反向、條件過濾等。
?3.簡化聚合類設計:將遍歷邏輯從聚合類中分離。
經典場景
- STL容器的迭代器(如std::vector::iterator)。
- 自定義集合類(如鏈表、圖)的遍歷。
- 數據庫查詢結果的逐行遍歷。
2. 模式結構解析
UML類圖
+---------------------+ +---------------------+
| Aggregate | | Iterator |
+---------------------+ +---------------------+
| + createIterator() |<>------->| + next(): void |
+---------------------+ | + hasNext(): bool | ^ +---------------------+ | ^ | | +-------+-------+ +---------+---------+ | | | |
+---------------------+ +-------------------+ +----------------+
| ConcreteAggregate | | ConcreteIterator | | Client |
+---------------------+ +-------------------+ +----------------+
| + createIterator() | | + next() | | 通過迭代器遍歷聚合對象 |
+---------------------+ | + hasNext() | +----------------+ +-------------------+
角色說明
Aggregate
:聚合接口,定義創建迭代器的方法(如createIterator()
)。- ?
ConcreteAggregate
:具體聚合類,實現迭代器創建邏輯。 Iterator
:迭代器接口,定義遍歷方法(如next()
、hasNext()
)。ConcreteIterator
:具體迭代器,實現特定遍歷邏輯。Client
:通過迭代器訪問聚合對象,無需依賴其內部結構。
3. 現代C++實現示例
場景:自定義鏈表迭代器
步驟1:定義鏈表節點與聚合類
#include <iostream>
#include <memory> template <typename T>
class ListNode {
public: T value; std::shared_ptr<ListNode<T>> next; ListNode(T val) : value(val), next(nullptr) {}
}; // 聚合類:單向鏈表
template <typename T>
class LinkedList {
public: void append(T value) { auto newNode = std::make_shared<ListNode<T>>(value); if (!head_) { head_ = newNode; } else { tail_->next = newNode; } tail_ = newNode; } // 創建正向迭代器 class Iterator; Iterator begin() { return Iterator(head_); } Iterator end() { return Iterator(nullptr); } private: std::shared_ptr<ListNode<T>> head_ = nullptr; std::shared_ptr<ListNode<T>> tail_ = nullptr;
};
步驟2:實現迭代器類
template <typename T>
class LinkedList<T>::Iterator {
public: Iterator(std::shared_ptr<ListNode<T>> node) : current_(node) {} T& operator*() const { return current_->value; } Iterator& operator++() { if (current_) current_ = current_->next; return *this; } bool operator!=(const Iterator& other) const { return current_ != other.current_; } private: std::shared_ptr<ListNode<T>> current_;
};
步驟3:客戶端代碼
int main() { LinkedList<int> list; list.append(1); list.append(2); list.append(3); // 使用范圍for循環(依賴begin()和end()) for (auto num : list) { std::cout << num << " "; // 輸出:1 2 3 } // 手動迭代 auto it = list.begin(); while (it != list.end()) { std::cout << *it << " "; ++it; }
}
擴展:反向迭代器
template <typename T>
class LinkedList<T>::ReverseIterator {
public: ReverseIterator(std::shared_ptr<ListNode<T>> head) { // 遍歷鏈表,將節點指針存入棧以實現反向 auto curr = head; while (curr) { stack_.push(curr); curr = curr->next; } } T& operator*() { return stack_.top()->value; } ReverseIterator& operator++() { if (!stack_.empty()) stack_.pop(); return *this; } bool operator!=(const ReverseIterator& other) { return !stack_.empty() || !other.stack_.empty(); } private: std::stack<std::shared_ptr<ListNode<T>>> stack_;
};
4. 應用場景示例
場景1:樹結構的深度優先遍歷
class TreeNode {
public: int value; std::vector<std::shared_ptr<TreeNode>> children;
}; class DepthFirstIterator {
public: DepthFirstIterator(std::shared_ptr<TreeNode> root) { stack_.push(root); } std::shared_ptr<TreeNode> next() { auto node = stack_.top(); stack_.pop(); for (auto it = node->children.rbegin(); it != node->children.rend(); ++it) { stack_.push(*it); } return node; } bool hasNext() { return !stack_.empty(); } private: std::stack<std::shared_ptr<TreeNode>> stack_;
};
場景2:過濾迭代器(條件遍歷)
template <typename T, typename Predicate>
class FilterIterator {
public: FilterIterator(typename LinkedList<T>::Iterator it, Predicate pred) : it_(it), pred_(pred) { // 找到第一個滿足條件的元素 while (it_ != end_ && !pred_(*it_)) ++it_; } T& operator*() { return *it_; } FilterIterator& operator++() { do { ++it_; } while (it_ != end_ && !pred_(*it_)); return *this; } bool operator!=(const FilterIterator& other) { return it_ != other.it_; } private: typename LinkedList<T>::Iterator it_; typename LinkedList<T>::Iterator end_; Predicate pred_;
}; // 使用示例:遍歷鏈表中的偶數
auto isEven = [](int x) { return x % 2 == 0; };
FilterIterator<int, decltype(isEven)> begin(list.begin(), isEven);
FilterIterator<int, decltype(isEven)> end(list.end(), isEven);
while (begin != end) { std::cout << *begin << " "; ++begin;
}
5. 優缺點分析
?優點 | ?缺點 |
---|---|
解耦遍歷邏輯與數據結構 | 增加類的數量(迭代器與聚合類需配對) |
支持多種遍歷策略(正向、反向等) | 復雜數據結構迭代器實現成本高(如圖遍歷) |
隱藏聚合對象內部實現 | 部分語言/框架已內置迭代器(如STL) |
6. 調試與優化策略
調試技巧(VS2022)?
1.?驗證迭代器有效性:
- 在迭代器越界時觸發斷言:
T& operator*() { assert(current_ != nullptr && "迭代器越界!"); return current_->value;
}
2. ?檢查迭代器狀態:
- 在
operator++()
中設置斷點,觀察指針移動是否符合預期。
性能優化
1. 預計算遍歷路徑:
- 對樹或圖的遍歷,預計算路徑并緩存結果(如廣度優先遍歷隊列)。
2. 內存連續性優化:
- 使用std::vector存儲節點,利用內存局部性提升遍歷速度。