首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 开发语言 > C语言 >

实现一个数据结构的迭代子,需要定义哪些东西?解决方案

2012-03-03 
实现一个数据结构的迭代子,需要定义哪些东西?对于这个哈希表的迭代子,初步这么设想:/*hashtableiterator*/

实现一个数据结构的迭代子,需要定义哪些东西?
对于这个哈希表的迭代子,初步这么设想:

/*hash   table   iterator*/
struct   hash_table_iterator
{
struct   mapping*   mp;   /*迭代子当前的位置*/
int   size;   /*遍历哈希表的大小*/
};

/*新建一个哈希表迭代子*/
struct   hash_table_iterator*   hash_table_iterator_new(struct   hash_table*);

/*遍历哈希表,遍历结束返回0,否则1*/
int   hash_table_iterator_step(struct   hash_table_iterator*);

/*释放哈希迭代子*/
void   hash_table_iterator_free(struct   hash_table_iterator*);

用起来如下:
struct   hash_table_iterator   iterator   =   hash_table_iterator_new(ht);
while(hash_table_iterator_step(iterator))
{
        //do   ...
}
hash_table_iterator_free(iterator);


[解决办法]
http://www.cuj.com/experts/1901/austern.htm?topic=experts

----------------------------------
写一个iterator并不难,并且它是扩展C++标准运行库的一个自然方式。但如果你想做正确,还是一些应该知道的关键点的。

标准C++运行库被设计得可扩展的:标准算法(如reverse()和partition())对预定义的容器(如vector和list)进行操作,并且它们也可以操作于用户自定义的提供了适当iterator的数据结构。对运行库的高效使用包括对它的扩充。

现如今,iterator对绝大多数的C++程序员都不陌生了。Iterator抽象了指针的绝大部分基本特征:forward iterator指向序列中的某个对象,而且它能进行增量运算以指向序列中的下一个元素。(更强的iterator范畴,bidirectional iterator和random iterator提供了额外的遍历序列的方法。更弱的iterator范畴则对绝大多数的数据结构都是不合适的。)

每iterator都有一个范畴类型category_type(对forward iterator来说则是std::forward_iterator_tag),一个value_type (它所指向的对象的类型),一个difference_type(表示序列中两个元素间距离的一个整数类型),以及一个pointer_type和reference_type(指向iterator的value_type的指针和引用)。这些类型都可通过std::iterator_traits来访问;当你定义自己的iterator类时,提供它们的最容易的方法是提供嵌套的typedefs: iterator_category,value_type,difference_type,pointer,和reference。

所谓forward iterator就是任何满足C++标准§24.1.3中的需求的类型;标准的那个小节告诉了你要定义什么成员函数和重载哪些运算符。一旦你已经知道iterator需要掌握的信息(以使得它能指向一个元素和找到其下一个元素),定义一个forward iterator只不过是对这些成员函数进行填空。

配对的Iterator
使问题变复杂的一个因素是通常定义一个iterator类是不够的。你可能需要定义两个iterator类,一个允许修改它所指向的对象(*i返回对象的引用),而另一个不允许(*i返回一个const的引用)。运行库预定义的容器类这么做了:举例来说,std::list类,有一个嵌套类型 iterator和另外一个不同的嵌套类型const_iterator;后者可以用来遍历const std::list。list <T> ::iterator和list <T> ::const_iterator的value_type都是T,但reference_type和pointer_type不同:对list <T> ::iterator它们分别是T&和T*,而对list <T> ::const_iterator它们是const T&和const T*。你能将一个list <T> ::iterator转换到一个list <T> ::const_iterator,但(由于const 的正确性的明显理由)不能反过来。

配对的iterator在用户自定义类型中如同在标准运行库预定义类型中一样常见。举例来说,假设你正在定义一个简单的单向链表类。你可能这样开始:

template <class T>

struct slist_node {

T val;

slist_node* next;

slist_node

(const T& t, slist_node* p)

: val(t), next(p) { }

};

template <class T> struct slist {

slist_node <T> * head;

...

};

供slist用的iterator类同样简单:

template <class T>

struct slist_iterator {

typedef std::forward_iterator_tag

iterator_category;

typedef T value_type;

typedef std::ptrdiff_t

difference_type;

typedef T& reference;

typedef T* pointer;

slist_iterator(slist_node <T> * x=0)

: p(x) { }

slist_iterator

(const slist_iterator& i)

: p(i.p) { }

reference operator*() const

{ return p-> val; }

pointer operator-> () const

{ return &(p-> val); }

slist_iterator& operator++() {

p = p-> next;

return *this;



}

slist_iterator operator++(int) {

slist_iterator tmp(*this);

++*this;

return tmp;

}

slist_node <T> * p;

};

我们该如何定义相应的const iterator?我们可以定义一个独立的 slist_const_iterator类,但代码重复造成浪费和易于出错。将slist_iterator变成slist_const_iterator时,变化是极小的:

l 申明p的类型为const slist_node <T> *而不是slist_node <T> *。

l 申明pointer和reference为const T*和const T&。

l 定义一个转换构造函数,它接受一个slist_iterator类型的参数。

这并不妨碍定义单个类来同时取代slist_iterator和slist_const_iterator。我们将iterator定义为接受一些额外模板参数,这些参数决定它是否为一个const iterator。我们给这个类一个带非const版本的参数的构造函数;一种情况下它将成为一个拷贝构造函数,而另一种情况下它将成为一个转换构造函数。另外二个差异只涉及用一个类型代替另外一个,因此很容易将它们封装入模板参数。

最后:那些额外的模板参数应该看起来象什么?在我的书中[注1],我建议pointer和reference类型作为模板参数显式传入。那个方法是可以的,但是它造成了多少有些笨重的类型名称;有一个整洁的解决方案。我们可以只提供一个额外模板参数,一个布尔标志,以决定我们是否正在定义const iterator,然后使用一点小技巧:“编译期的? : 操作”以根据此标志选择一个类型或另一个。这展示于Listing 1。

等于比较
我们还没有定义一个相等操作。这中间还隐藏着一个问题,而你甚至能在一些标准运行库预定义的iterator中发现它。试着编译这一个程序:

#include <deque>

int main() {

std::deque <int> d;

std::deque <int> ::const_iterator i = d.begin();

while (d.end() != i)

++d;

}

程序没有做任何事,但问题不在于这一点。重要的是,对很多现存的运行库的实作,它甚至不能编译。那些实作有bug吗?不一定;i的类型是deque <int> ::const_iterator,而d.begin()返回一个deque <int> ::iterator,C++标准没有明确这两者之间的等于比较是否保证能工作[注3]。然而,即使标准没有明确要求这一点,如果你在你自己的iterator类中支持它的话,当然会更友好。

你可能想知道这怎么会成为问题。毕竟,我们不是已经说了容器的 iterator类型总能被转换到它的const iterator类型吗?如果d.begin()能转换成deque <> ::const_iterator,那么为什么不能比较他们?

问题是有许多的不同方法来定义iterator的相等操作;如果它们是按两种最显而易见的方法来定义的,容器的iterator和const iterator类型之间的比较将不能工作。

首先,假设operator==()被定义为成员函数。这不足够好。如果i是 deque <> ::const_iterator,而j是deque <> ::iterator,那么i == j可以工作而j == i不能。很容易明白不对称的原因:成员函数天生就是不对称的。a.f(b)这样的表达式(或,对本例,j.operator==(i))调用特定类的成员函数;转换只发生在函数的参数上。

热点排行