迭代器簡介
c++中經(jīng)常會(huì)用到這樣的寫法:
std::vector<int> vec; // 假設(shè)其中已經(jīng)有數(shù)據(jù)
for (auto it = vec.begin(); it != vec.end(); ++it) {
cout << "value is:" << *it << endl;
}
上面代碼中,it即是迭代器叠国,通過迭代器自增可以遍歷vector容器驶忌,而通過解引用(*)可以訪問當(dāng)前迭代器指向的數(shù)據(jù)。這樣看囤屹,迭代器有點(diǎn)類似于數(shù)組的指針熬甚。
而range-based循環(huán)實(shí)際上也是依賴迭代器:
for (auto value : vec) {
// ...
}
這種寫法實(shí)際上等同于上面的顯示使用迭代器。
那么肋坚,如何實(shí)現(xiàn)自己的迭代器呢乡括?下面通過一個(gè)真實(shí)的例子一步步實(shí)現(xiàn)。
案例描述
假設(shè)我們有一個(gè)批量接口智厌,每次只能處理100個(gè)id诲泌。函數(shù)簽名如下:
void HandleIds(const vector<int> &ids);
現(xiàn)在我們有1萬條id需要查詢,就要分100次進(jìn)行查詢铣鹏。那么我們首先要將其分成100組敷扫。當(dāng)然這并不困難,但作為一名優(yōu)秀的程序員诚卸,絕不能容忍每次都這樣的重復(fù)性工作葵第。于是我們希望做一個(gè)足夠好用的工具,它可以這樣使用:
std::vector<int> all_ids; // 假設(shè)其中已經(jīng)有數(shù)據(jù)
for (const auto &chunk : ChunkSpliter(all_ids, 100)) {
HandleIds(chunk); // chunk中包含100個(gè)id
}
需求分析
分析一下上述效果合溺,我們發(fā)現(xiàn):
- ChunkSpliter是一個(gè)類卒密,構(gòu)造函數(shù)包含兩個(gè)參數(shù)
- ChunkSpliter的實(shí)例是可迭代對象
- 對迭代器解引用,可以得到std::vector<int>對象
對于第1點(diǎn)辫愉,只需定義一個(gè)ChunkSpliter類栅受,并顯示聲明構(gòu)造函數(shù)即可。
對于第2點(diǎn),如何寫一個(gè)可迭代對象屏镊?其實(shí)只要包含begin()和end()方法依疼,返回迭代器即可。但這里要返回什么迭代器呢而芥?能否使用vector本身的迭代器律罢?
看到第3點(diǎn),我們就知道棍丐,必須自定義迭代器误辑,并重載解引用運(yùn)算符 operator*(),這樣才能實(shí)現(xiàn)迭代器解引用后得到我們希望的std::vector<int>對象歌逢,即一個(gè)包含100個(gè)id的分片巾钉。
開始開發(fā)
ChunkSpliter類與構(gòu)造函數(shù)
首先我們聲明ChunkSpliter類,并聲明構(gòu)造函數(shù):
class ChunkSpliter {
public:
explicit ChunkSpliter(const std::vector<int> &container, const uint32_t &chunk_size = 100)
: container_(container), chunk_size_(chunk_size) {}
private:
const std::vector<int>& container_;
int chunk_size_;
};
在構(gòu)造函數(shù)中秘案,我們將傳入的container和chunk_size存儲(chǔ)到了類的成員變量中砰苍,以便后續(xù)在迭代器中使用。
開發(fā)迭代器
如果將一個(gè)類作為迭代器阱高,需要重載下面這些運(yùn)算符
- 自增 ++
- 等于/不等 == !=
- 解引用 *
對于我們這個(gè)需求赚导,自增需要每次跳過100個(gè)數(shù)據(jù),解引用需要生成一個(gè)std::vector<int>對象赤惊。
另外吼旧,我們也需要保存原始的數(shù)組,以便解引用時(shí)生成它的一個(gè)切片未舟。
于是我們聲明如下:
class ChunkIterator {
public:
explicit ChunkIterator(const std::vector<int> &container, int index, int chunk_size)
: container_(container), index_(index), chunk_size_(chunk_size) {}
bool operator==(const ChunkIterator& other) const { return index_ == other.index_;}
bool operator!=(const ChunkIterator& other) const { return index_ != other.index_;}
ChunkIterator& operator++();
std::vector<int> operator*();
private:
const std::vector<int>& container_;
int index_;
int chunk_size_;
};
其中圈暗,++ 和 * 運(yùn)算符和沒有定義,我們在類外定義:
// 重載自增運(yùn)算符 ++
ChunkIterator& ChunkIterator::operator++() {
index_ += chunk_size_;
if (index_ > container_.size()) {
index_ = container_.size();
}
return *this;
}
// 重載解引用運(yùn)算符 *
std::vector<int> ChunkIterator::operator*() {
std::vector<int> chunk;
for (int i=index_; i<index_+100 && i <container_.size(); i++) {
chunk.push_back(container_[i]);
}
return chunk;
}
最后处面,為了上ChunkSpliter實(shí)例能成為可迭代對象厂置,我們需要聲明begin() 和 end()方法,返回ChunkIterator類型的迭代器:
class ChunkIterator {
// ...
public:
ChunkIterator begin() {return ChunkIterator(container_, 0, chunk_size_);}
ChunkIterator end() {return ChunkIterator(container_, container_.size(), chunk_size_);}
};
使用
好了魂角,我們的代碼已經(jīng)完成了昵济,讓我們試試效果吧:
int main() {
std::vector<int> ids;
for (int i = 0; i<1000; i++) {
ids.push_back(i);
}
for (auto chunk : ChunkSpliter(ids, 100)) {
for (auto v : chunk) {
cout << v << " ";
}
cout << endl;
}
}
進(jìn)階
為了進(jìn)一步提高我們工具的泛用性,讓它不局限于std::vector<int>這一種類型野揪,我們可以使用模板访忿。聰明的讀者,試一下如何做到吧斯稳。