添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

第8篇:C++容器对象的枚举模式

11 个月前 · 来自专栏 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 );