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,那麼問題來了。
為什麼不把遍歷方法寫在Shelf裡面?
作為類的設計者,無法瞭解過多的應用場景,只能是提供最基本的介面。比如對於Shelf的遍歷可能有很多種,輸出所有元素,輸出所有元素+逗號,輸出所有元素+\ ……所有,遍歷的場景可能有很多,這個不應該由類的設計者來提供。所以,不能寫在類的裡面。既然不使用迭代器可以遍歷,那麼為什麼還要使用迭代器遍歷
先說一個從文章開始處的連結中學到的東西,對於非迭代器遍歷的方法,traverse需要知道shelf_的型別,那麼如果現在不用vector< Book >去實現,而是換成了list< Book >,那麼類的使用者需要該這裡的引數型別,相當於向類的使用者暴露了過多的內部資訊。其實,我覺得這個還倒是其次,這麼做沒有遮蔽實現的細節,通常情況下,類的使用者是不需要關注類的實現細節的,所以這麼做不好。為什麼迭代器遍歷的方式好?
有了上文的分析,就很容易看出,及時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;
}