返回"任何类型的输入迭代器",而不是vector :: iterator或者list :: iterator
假设我想在C ++中实现一个数据结构来存储面向图。借助STL容器,弧将存储在节点中。我希望用户能够以类似STL的方式遍历节点的弧线。
我遇到的问题是,我不想在Node类(实际上将是抽象基类)中公开我将在具体类中实际使用的STL容器。因此我不想让我的方法返回std :: list :: iterator或者std :: vector :: iterator ...
我尝试了这个:
class Arc; typedef std::iterator<std::random_access_iterator_tag, Arc*> ArcIterator; // Wrong! class Node { public: ArcIterator incomingArcsBegin() const { return _incomingArcs.begin(); } private: std::vector<Arc*> _incomingArcs; };
但这是不正确的,因为不能使用vector :: const_iterator创建ArcIterator。那么这个ArcIterator可以是什么呢?
我找到了有关STL的自定义迭代器的本文,但没有帮助。我今天一定有点沉重...;)
解决方案
如果我们确实不希望该类的客户端知道它在下面使用了向量,但仍然希望他们能够以某种方式对其进行迭代,则很可能需要创建一个将其所有方法转发给std的类。 :: vector :: iterator。
一种替代方法是根据应在下面使用的容器类型对Node进行模板化。然后,客户会明确告知其使用的是哪种类型的容器,因为他们告诉他们使用它。
就我个人而言,我认为将向量封装在远离用户的地方通常没有任何意义,但是仍然提供了大部分(甚至某些)界面。它的封装层太薄,无法真正提供任何好处。
我想认为应该有一种通过直接STL来实现此目的的方法,类似于我们尝试执行的操作。
如果没有,我们可能想研究使用boost的迭代器外观和适配器,我们可以在其中定义自己的迭代器或者将其他对象改编为迭代器。
试试这个:
class Arc; class Node { private: std::vector<Arc*> incoming_; public: typedef std::vector<Arc*>::iterator iterator; iterator incoming_arcs_begin() { return incoming_.begin(); } };
并在其余代码中使用Node :: iterator。当/如果我们更改容器,则必须在一个地方更改typedef。 (我们可以使用存储的添加typedef(在这种情况下为vector)进一步执行此步骤。)
至于const问题,可以将vector的const_iterator定义为迭代器,或者像vector一样定义双精度迭代器类型(const和非const版本)。
我查看了头文件VECTOR。
vector<Arc*>::const_iterator
是用于的typedef
allocator<Arc*>::const_pointer
可以是ArcIterator吗?喜欢:
typedef allocator<Arc*>::const_pointer ArcIterator;
为了隐藏迭代器基于std :: vector <Arc *> :: iterator
的事实,我们需要一个委托给std :: vector <Arc *> :: iterator
的迭代器类。 std :: iterator
不这样做。
如果查看编译器的C ++标准库中的头文件,可能会发现std :: iterator
本身并不是很有用,除非我们只需要一个为iterator_category
,value_type定义typedef的类即可。
等
正如Doug T.在他的回答中提到的那样,boost库具有使编写迭代器更容易的类。特别是,如果我们希望迭代器在取消引用时返回" Arc"而不是" Arc *",则" boost :: indirect_iterator"可能会有所帮助。
我们可以对Node类进行模板化,并在其中对Iterator和const_iterator都进行类型定义。
例如:
class Arc {}; template< template<class T, class U> class Container = std::vector, class Allocator = std::allocator<Arc*> > class Node { public: typedef typename Container<Arc*, Allocator>::iterator ArcIterator; typedef typename Container<Arc*, Allocator>::Const_iterator constArcIterator; constArcIterator incomingArcsBegin() const { return _incomingArcs.begin(); } ArcIterator incomingArcsBegin() { return _incomingArcs.begin(); } private: Container<Arc*, Allocator> _incomingArcs; };
我没有尝试过这段代码,但是它给了你这个主意。但是,我们必须注意,使用ConstArcIterator只会禁止修改指向Arc的指针,而不是修改Arc本身(例如,通过非const方法)。
C ++ 0x将允许我们使用自动类型确定来执行此操作。
在新标准中,
对于(vector :: const_iterator itr = myvec.begin(); itr!= myvec.end(); ++ itr可以用这个代替 对于(自动itr = myvec.begin(); itr!= myvec.end(); ++ itr)
同样,我们将能够返回任何合适的迭代器,并将其存储在" auto"变量中。
在新标准启动之前,我们将不得不对类进行模板化,或者提供一个抽象接口来访问列表/向量的元素。例如,我们可以通过将迭代器存储在成员变量中并提供成员函数(例如begin()
和next()
)来实现此目的。当然,这意味着一次只能循环一次,就可以安全地遍历元素。
看看Adobe的any_iterator
:此类使用一种称为类型擦除的技术,通过该技术,底层迭代器类型被隐藏在抽象接口的后面。当心:使用any_iterator会由于虚拟调度而导致运行时间的损失。
好吧,因为保证" std :: vector"具有连续的存储,所以这样做应该是完美的:
class Arc; typedef Arc* ArcIterator; class Node { public: ArcIterator incomingArcsBegin() const { return &_incomingArcs[0] } ArcIterator incomingArcsEnd() const { return &_incomingArcs[_incomingArcs.size()] } private: std::vector<Arc*> _incomingArcs; };
基本上,指针的功能就像随机访问迭代器一样足够,足以替代它们。
考虑使用Visitor Pattern(访问者模式)并反转关系:我们不向图结构询问数据容器,而是给图提供了仿函数,然后让图将该仿函数应用于其数据。
访客模式是图形上的常用模式,请查阅关于访客概念的boost的图形库文档。