迭代器實(shí)戰(zhàn)——數(shù)組分塊工具

迭代器簡介

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):

  1. ChunkSpliter是一個(gè)類卒密,構(gòu)造函數(shù)包含兩個(gè)參數(shù)
  2. ChunkSpliter的實(shí)例是可迭代對象
  3. 對迭代器解引用,可以得到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>這一種類型野揪,我們可以使用模板访忿。聰明的讀者,試一下如何做到吧斯稳。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末海铆,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子挣惰,更是在濱河造成了極大的恐慌卧斟,老刑警劉巖殴边,帶你破解...
    沈念sama閱讀 218,607評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異珍语,居然都是意外死亡锤岸,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,239評論 3 395
  • 文/潘曉璐 我一進(jìn)店門板乙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來是偷,“玉大人,你說我怎么就攤上這事募逞〉懊” “怎么了?”我有些...
    開封第一講書人閱讀 164,960評論 0 355
  • 文/不壞的土叔 我叫張陵放接,是天一觀的道長刺啦。 經(jīng)常有香客問我,道長透乾,這世上最難降的妖魔是什么洪燥? 我笑而不...
    開封第一講書人閱讀 58,750評論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮乳乌,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘市咆。我一直安慰自己汉操,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,764評論 6 392
  • 文/花漫 我一把揭開白布蒙兰。 她就那樣靜靜地躺著磷瘤,像睡著了一般。 火紅的嫁衣襯著肌膚如雪搜变。 梳的紋絲不亂的頭發(fā)上采缚,一...
    開封第一講書人閱讀 51,604評論 1 305
  • 那天,我揣著相機(jī)與錄音挠他,去河邊找鬼扳抽。 笑死,一個(gè)胖子當(dāng)著我的面吹牛殖侵,可吹牛的內(nèi)容都是我干的贸呢。 我是一名探鬼主播,決...
    沈念sama閱讀 40,347評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼拢军,長吁一口氣:“原來是場噩夢啊……” “哼楞陷!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起茉唉,我...
    開封第一講書人閱讀 39,253評論 0 276
  • 序言:老撾萬榮一對情侶失蹤固蛾,失蹤者是張志新(化名)和其女友劉穎结执,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體艾凯,經(jīng)...
    沈念sama閱讀 45,702評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡昌犹,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,893評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了览芳。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片斜姥。...
    茶點(diǎn)故事閱讀 40,015評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖沧竟,靈堂內(nèi)的尸體忽然破棺而出铸敏,到底是詐尸還是另有隱情,我是刑警寧澤悟泵,帶...
    沈念sama閱讀 35,734評論 5 346
  • 正文 年R本政府宣布杈笔,位于F島的核電站,受9級(jí)特大地震影響糕非,放射性物質(zhì)發(fā)生泄漏蒙具。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,352評論 3 330
  • 文/蒙蒙 一朽肥、第九天 我趴在偏房一處隱蔽的房頂上張望禁筏。 院中可真熱鬧,春花似錦衡招、人聲如沸篱昔。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,934評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽州刽。三九已至,卻和暖如春浪箭,著一層夾襖步出監(jiān)牢的瞬間穗椅,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,052評論 1 270
  • 我被黑心中介騙來泰國打工奶栖, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留匹表,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,216評論 3 371
  • 正文 我出身青樓驼抹,卻偏偏與公主長得像桑孩,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子框冀,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,969評論 2 355

推薦閱讀更多精彩內(nèi)容