本文记录我对迭代器的一点理解。先列出参考的文章[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;
}