第8篇:C++容器对象的枚举模式
C++迭代模式有着比C#更为丰富的迭代接口
- begin() 成员函数,返回指向容器起始元素的迭代器。跟C#的 IEnumerable<T>::GetEnumerator() 作用大同小异。
- end() 成员函数,返回指向容器末端边界的迭代器。逻辑上指向一个空指针nullptr。
- operator++相当于C#的 IEnumerator<T>::MoveNext()
- operator* 和 operator-> 相当于C#的 IEnumerator<T>::Current
- 来自 operator* 的引用返回类型——而不是 IList<T> 索引器设置器
- operator== 和 operator!= 是C++中内置的操作符。与容器的 end() 配合使用可以实现类似 IEnumerator<T>::MoveNext() 返回值
如果您定义了这些,那么标准算法将适用于您的容器和迭代器。 就不需要定义接口,不需要虚函数。没有虚函数的情况下, C++ 通用代码比等效的 .NET 代码更快。
注意:在编写泛型算法时,最好使用 std::begin(container) 和 std::end(container) 而不是容器成员函数。
然而事实上,C++除了使用标准算法的迭代接口之外,你也可以自己手动定义枚举接口IEnumator<T>、以及可枚举对象IEnumerable<T>,只要你明白C#枚举接口的实现原理。
下面是一个典型的示例
// 设计所有迭代器应该有的接口。实际的 C# 具有以下接口。
// 在 C# 1.0 中,我将它作为顶级 Object 类接收并进行类型转换...
// 在 C# 2.0 中,随着 Generic 的引入,迭代器接口变得更加方便。
template<typename T>
struct IEnumerator{
virtual bool moveNext() = 0;
virtual T getItem() = 0;
virtual void reset() = 0;
接下来,任何可以有迭代器的容器都必须提供下面的接口。这个抽象接口用来返回一个可枚举的对象IEnumerable<T>。
template<typename T>
class IEnumerable{
public:
virtual IEnumerator<T>* getEnumerator() = 0;
那什么是可枚举对象呢?很少读物有明确的定义。其实笔者的理解, 可枚举对象就是具有同质(单一数据类型)元素,并且各个元素之间传在链接关系的容器对象 。例如所有的线性表结构:如vector、linkedlist、double-linklist、array等。理解好IEnumerable<T>这个接口声明就足够了。
另外,任何可容器内部必须做两件事
- 实现IEnumerator<T>抽象接口中的moveNext()方法、getItem()方法、和reset()方法.即实现具体的枚举器。
- 实现getEnumerator()方法,并且将IEnumerator<T>的具体实现的枚举器返回。
template<typename T>
class LinkList : public IEnumerable<T>{
struct Node{
T data;
Node* next;
Node( T a, Node* n ) : data(a), next(n) {}
Node* head;
public:
LinkList() : head(0) {}
void push_back(T a){
head = new Node( a, head );
//从LinkList中获取值的迭代器
class ListEnumerator : public IEnumerator<T>{
Node* current;
Node* head;
public:
ListEnumerator( Node* init = 0 )
: head(init), current(init) {}
bool moveNext(){
current = current->next;
return current != 0;
T getItem() { return current->data; }
void reset() { current = head; }
//创建一个返回迭代器的函数。
IEnumerator<T>* getEnumerator(){
return new ListEnumerator( head );
调用代码
// 使用给定的迭代器对所有元素求和
template<typename T> void sum( IEnumerator<T>* p ){
T s = T();
p->reset();
s += p->getItem();
} while (p->moveNext());
cout << s << endl;
template<typename T> void sum2( IEnumerable<T>& c ){
IEnumerator<T>* p = c.getEnumerator();
sum( p );
int main(){
LinkList<int> s;
s.push_back( 10 );
s.push_back( 20 );
s.push_back( 30 );
s.push_back( 40 );
s.push_back( 23 );
s.push_back( 62 );
s.push_back( 17 );
s.push_back( 32 );
s.push_back( 55 );