benxintuzi

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

模式动机(Iterator Pattern)对于聚合类的对象进行访问时,比如list,queue等,我们总希望有一个统一的访问接口,或者实现对多个聚合类对象的统一访问,或者实现对一个聚合类的多种不同访问方式,此时迭代器模式可以满足我们的这种需求。

  迭代器就是在不暴露聚合类对象内部结构的前提下,对外提供了一种访问该对象的方法。 

  一般而言,一个迭代器是和一个聚合类紧密相连的。如何实现只允许迭代器访问聚合类的内部数据而避免其他类访问呢?这里需要借助于C++中的友元类,令迭代器类为聚合类  的友元类即可实现这个目标。

 

具体迭代器实现时有如下问题需要考虑:

1》 迭代遍历过程由迭代器控制还是由用户控制

若由用户控制时,这种迭代器称为外部迭代器。此时,客户必须负责向前推进遍历过程,显式地向迭代器请求下一个元素;若由迭代器控制,则称为内部迭代器。此时,迭代器会自动对聚合对象中的每个元素执行客户指定的操作。

大多数情况下,外部迭代器比内部迭代器更加灵活,可适应多个不同类型的聚合类对象,且扩展更容易。

 

2》 遍历算法是由迭代器定义还是由聚合类本身定义

若迭代器负责遍历算法,那么很容易在相同的聚合类上使用不同的迭代算法,同时也易于在不同的聚合类上使用同一迭代算法;若聚合类本身定义迭代算法,那么迭代器只负责存储遍历的当前状态,相当于一个指向当前状态的指针,因此我们称这种迭代器为游标(cursor)。

为了扩展迭代器的通用性,我们经常使用多态迭代器类型,即同时拥有一个抽象迭代器接口和具体迭代器实现类。对于不同类型的聚合对象,可以使用不同类型的迭代器实现类,这样就需要用工厂模式来分配迭代器对象。此时又有一个问题,分配的迭代器是谁来负责删除。如果由用户负责,可能造成内存泄漏,因为程序可能很复杂而忘记delete,或者在delete之前程序发生了异常。那么如何解决呢?

方法就是使用代理模式在栈上分配一个迭代器代理,由该代理负责删除堆上分配的空间。这样当程序结束时,所有分配的空间都会得到释放。要定义这种迭代器代理,必须实现如下操作:

1>  支持解引用操作 *

2>  赋值操作 =

3>  ->操作

4>  比较操作,如!= / ==

5>  自增操作 ++

为了代码的简洁性,我们只重载了 -> 操作,另外需要将复制构造函数和赋值操作运算符声明为private,这样既可以防止多次delete同一对象,又可以防止编译器提供的默认实现。

 

模式结构图:

 

模式代码:

bt_迭代模式.h:

 1 #ifndef IP_H
 2 #define IP_H
 3 #include <iostream>
 4 #include <string>
 5 using namespace std;
 6 
 7 /*
 8     抽象迭代器
 9 */
10 class Iterator
11 {
12 public:
13     virtual ~Iterator(){ }
14     virtual void First() = 0;
15     virtual void Next() = 0;
16     virtual bool IsDone() = 0;
17     virtual char CurrentItem() = 0;
18 };
19 
20 /*
21     抽象聚合类
22 */
23 class Aggregate
24 {
25 public:
26     virtual ~Aggregate(){ }
27     virtual Iterator* CreateIterator() = 0;
28     virtual int Count() const = 0;
29     virtual char Get(int index) const = 0;
30 };
31 
32 
33 /*
34     具体迭代器
35 */
36 class ConcreteIterator : public Iterator
37 {
38 public:
39     ConcreteIterator(const Aggregate* a) : aggregate(a), currentIndex(0){ }
40     virtual void First(){ currentIndex = 0; }
41     virtual void Next(){ currentIndex++; }
42     virtual bool IsDone(){ return currentIndex >= aggregate->Count(); }
43     virtual char CurrentItem()
44     {
45         if(IsDone())
46         {
47             cout << "已遍历完毕" << endl;
48             return 0;
49         }
50         else
51             return aggregate->Get(currentIndex);
52     }
53 
54 private:
55     const Aggregate* aggregate;
56     int currentIndex;
57 };
58 
59 /*
60     具体聚合类
61 */
62 class ConcreteAggregate : public Aggregate
63 {
64 public:
65     ConcreteAggregate(string str){ name = str; }
66     virtual Iterator* CreateIterator()
67     {
68         return new ConcreteIterator(this);
69     }
70     virtual int Count() const{ return name.size(); }
71     virtual char Get(int index) const{ return name.at(index); };
72 
73 private:
74     string name;
75 };
76 
77 #endif // IP_H

 

测试用例.cpp:

 1 #include "bt_迭代器模式.h"
 2 int main()
 3 {
 4     cout << "***** 迭代器模式测试 *****" << endl;
 5     string name("benxintuzi");
 6     Aggregate* aggregate = new ConcreteAggregate(name);
 7     Iterator* iter = new ConcreteIterator(aggregate);
 8     for(iter->First(); !iter->IsDone(); iter->Next())
 9         cout << iter->CurrentItem() << endl;
10 
11     delete iter;
12     delete aggregate;
13 
14     return 0;
15 }

 

模式扩展:

将迭代器模式进行扩展,使得可以通过迭代器代理自己释放分配的内存,而不是依赖客户的手动操作。定义代理类如下:

// ...

class IteratorPtr   /* 迭代器的代理类 */
{
public:
    IteratorPtr(Iterator* it) : iter(it){ }
    ~IteratorPtr(){ delete iter; }                 // 删除堆上的迭代器
    Iterator* operator->(){ return iter; }

private:
    IteratorPtr(const IteratorPtr&);
    IteratorPtr& operator=(const IteratorPtr&);

private:
    Iterator* iter;
};

// ...

int main()
{
    cout << "***** 迭代器代理模式测试 *****" << endl;
    string name("benxintuzi");
    Aggregate* aggregate = new ConcreteAggregate(name);
    IteratorPtr iterPtr(aggregate->CreateIterator());        // 对迭代器进行包装,并且代理类指定在栈上,程序结束后代理类对象自动回收
    for(iterPtr->First(); !iterPtr->IsDone(); iterPtr->Next())
        cout << (iterPtr->CurrentItem()) << endl;

    return 0;
}

 

模式总结:

:: 支持以不同的方式遍历一个聚合类,只需定义抽象迭代器的具体实现类即可更改遍历方法。

:: 迭代器模式简化了聚合类的接口,使得聚合类本身不必再维护如何遍历的代价了。

:: 在同一个聚合类上,通过不同的迭代器实例可以同时执行多个遍历操作,互不影响。

 

 

 

posted on 2015-06-11 13:35  benxintuzi  阅读(683)  评论(0编辑  收藏  举报