1. 程式人生 > >c++學習-為什麼要用迭代器

c++學習-為什麼要用迭代器

本文記錄我對迭代器的一點理解。先列出參考的文章[LevelDB原始碼分析–使用Iterator簡化程式碼設計]。我對參考文章的內容自己做了實現,我講的不清楚的地方可以直接參考原文。

為什麼要設計迭代器這個東西

用迭代器可以很大程度上隔離容器底層實現,使用時只需依賴迭代器相對統一的方法/介面。

這是為什麼要設計迭代器這個東西,我在看ACC的時候,容器-迭代器-演算法這三者有著密不可分的關係,演算法通過迭代器作用於容器,迭代器相當於起到了媒介的作用,這是它的功能。但是,上面說的才是為什麼要設計這樣的一個東西。

程式碼

#include <iostream>
#include <string> #include <vector> class Book { public: Book(){} Book( const char* name ){ book_name_ = name; } friend std::ostream& operator<<( std::ostream& os, const Book& book ); private: std::string book_name_; }; std::ostream& operator<<( std
::ostream& os, const Book& book ){ if(os){ os << book.book_name_; } return os; } class Shelf { public: Shelf() {} public: typedef std::vector<Book>::iterator iter; iter begin() { return shelf_.begin(); } iter end() { return shelf_.end(); } std
::vector<Book> get_shelf() const { return shelf_; } void push_back( Book& book ){ shelf_.push_back( book ); } private: std::vector<Book> shelf_; }; typedef void (*callback)( Book& ); void traverse( std::vector<Book>& shelf, callback visit ){// 不使用迭代器遍歷 int sz = shelf.size(); for( int i = 0; i < sz; ++i ){ visit( shelf[i] ); } } void traverse1( Shelf::iter b, Shelf::iter e, callback visit ){// 使用迭代器遍歷 while( b != e ){ visit(*b); ++b; } } void visit(Book& book){ std::cout << book << std::endl; } int main(){ Book book1 = "C++"; Book book2 = "Java."; Book book3 = "Python"; Shelf shelf; shelf.push_back( book1 ); shelf.push_back( book2 ); shelf.push_back( book3 ); std::vector<Book> books = shelf.get_shelf(); traverse( books, visit ); traverse1( shelf.begin(), shelf.end(), visit ); return 0; }

說說上面的程式碼,我的目的是為了遍歷shelf裡面的book,那麼問題來了。

  1. 為什麼不把遍歷方法寫在Shelf裡面?
    作為類的設計者,無法瞭解過多的應用場景,只能是提供最基本的介面。比如對於Shelf的遍歷可能有很多種,輸出所有元素,輸出所有元素+逗號,輸出所有元素+\ ……所有,遍歷的場景可能有很多,這個不應該由類的設計者來提供。所以,不能寫在類的裡面。

  2. 既然不使用迭代器可以遍歷,那麼為什麼還要使用迭代器遍歷
    先說一個從文章開始處的連結中學到的東西,對於非迭代器遍歷的方法,traverse需要知道shelf_的型別,那麼如果現在不用vector< Book >去實現,而是換成了list< Book >,那麼類的使用者需要該這裡的引數型別,相當於向類的使用者暴露了過多的內部資訊。其實,我覺得這個還倒是其次,這麼做沒有遮蔽實現的細節,通常情況下,類的使用者是不需要關注類的實現細節的,所以這麼做不好。

  3. 為什麼迭代器遍歷的方式好?
    有了上文的分析,就很容易看出,及時Shelf的設計換成了list< Book >,遍歷的介面也任然不需要修改,因為迭代器型別遮蔽了底層實現的機制,使用者只需專注怎麼更好的使用即可。

用迭代器可以很大程度上隔離容器底層實現,使用時只需依賴迭代器相對統一的方法/介面

後記

看了leveldb迭代器設計之後,再看原文連結的感觸。

程式碼1

不採用迭代器的設計,客戶端暴露了內部的實現。

#include <iostream>
#include <string>
#include <vector>

namespace TJU{

class Shelf {
public:
    Shelf() {}
    void push_back(const std::string& book) { books_.push_back(book); }
    std::vector<std::string> get_books() const { return books_; }

private:
    std::vector<std::string> books_;
};// shelf

class BookStore {
public:
    BookStore() {}
    void push_back( const Shelf& shelf ) { shelfs_.push_back(shelf);  }
    std::vector<Shelf> get_shelfs() const { return shelfs_; }

private:
    std::vector<Shelf> shelfs_;

};// BookStore


}// namespace TJU

int main(){

    TJU::BookStore store;

    TJU::Shelf math;
    math.push_back( "linear algebra" );
    math.push_back( "calculus" );
    math.push_back( "statics" );

    TJU::Shelf cs;
    cs.push_back("java");
    cs.push_back( "c++" );
    cs.push_back( "AI" );

    store.push_back(math);
    store.push_back(cs);

    /* iterate the books */
    std::vector<TJU::Shelf> shelfs = store.get_shelfs();
    int sz = shelfs.size();
    for( int i = 0; i < sz; ++i ){
        std::vector<std::string> books = shelfs[i].get_books();
        int sz1 = books.size();
        for( int j = 0; j < sz1; ++j ){
            std::cout << books[j] << " ";
        }
        std::cout << std::endl;
    }

    return 0;
}

程式碼2

模仿我之前對於vector的實現,內部提供迭代器型別,供外部訪問。現在遍歷遮蔽了底層的實現。

#include <iostream>
#include <string>
#include <vector>

namespace TJU{

class Shelf {
public:
    Shelf() {}
    void push_back(const std::string& book) { books_.push_back(book); }
public:
    typedef std::vector<std::string>::iterator Iterator;
    Iterator begin() { return books_.begin(); }
    Iterator end() { return books_.end(); }

private:
    std::vector<std::string> books_;
};// shelf

class BookStore {
public:
    BookStore() {}
    void push_back( const Shelf& shelf ) { shelfs_.push_back(shelf);  }
public:
    typedef std::vector<Shelf>::iterator Iterator;
    Iterator begin() { return shelfs_.begin(); }
    Iterator end() { return shelfs_.end(); }
private:
    std::vector<Shelf> shelfs_;

};// BookStore


}// namespace TJU

int main(){

    TJU::BookStore store;

    TJU::Shelf math;
    math.push_back( "linear algebra" );
    math.push_back( "calculus" );
    math.push_back( "statics" );

    TJU::Shelf cs;
    cs.push_back("java");
    cs.push_back( "c++" );
    cs.push_back( "AI" );

    store.push_back(math);
    store.push_back(cs);

    /* iterate the books */
    TJU::BookStore::Iterator b = store.begin();
    TJU::BookStore::Iterator e = store.end();
    while( b != e ){
        TJU::Shelf::Iterator bb = b->begin();
        TJU::Shelf::Iterator ee = b->end();
        while( bb != ee ){
            std::cout << *bb << " ";
            ++bb;
        }
        std::cout << std::endl;
        ++b;
    }
    return 0;
}