返回"任何类型的输入迭代器",而不是vector :: iterator或者list :: iterator

时间:2020-03-06 14:39:38  来源:igfitidea点击:

假设我想在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 &lt;Arc *> :: iterator的事实,我们需要一个委托给std :: vector &lt;Arc *> :: iterator的迭代器类。 std :: iterator不这样做。

如果查看编译器的C ++标准库中的头文件,可能会发现std :: iterator本身并不是很有用,除非我们只需要一个为iterator_categoryvalue_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的图形库文档。