English 中文(简体)
返回任何类型的输入迭代器,而不是向量:迭代器或列表::迭代程序[重复]
原标题:
  • 时间:2008-09-24 13:07:35
  •  标签:

假设我想在C++中实现一个数据结构来存储面向图。由于STL容器,圆弧将存储在节点中。我希望用户能够以类似STL的方式在节点的弧上迭代。

我遇到的问题是,我不想在Node类(实际上是一个抽象基类)中公开我将在具体类中实际使用的STL容器。因此,我不想让我的方法返回std::list::iterator或std::vector::迭代器。。。

我试过了:

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的自定义迭代程序,但它没有帮助。我今天一定有点重…;)

问题回答

试试这个:

class Arc;
class Node {
private:
  std::vector<Arc*> incoming_;
public:
  typedef std::vector<Arc*>::iterator iterator;
  iterator incoming_arcs_begin()
  { return incoming_.begin(); }
};

并在代码的其余部分使用Node::迭代器。当/如果您更改容器,您必须在一个地方更改typedef。(您可以通过为存储添加额外的typedef(在本例中为vector)来进一步实现这一点。)

至于常量问题,可以将vector的const_iterator定义为迭代器,也可以像vector那样定义双迭代器类型(常量和非常量版本)。

看看Adobe的any_iterator:此类使用一种名为类型擦除的技术,通过该技术,底层迭代器类型隐藏在抽象接口后面。注意:使用any_iteror会因虚拟调度而导致运行时损失。

我想应该有一种方法通过直接STL来实现这一点,类似于你正在尝试做的事情。

如果没有,您可能需要考虑使用boost s迭代器外观和适配器,您可以在其中定义自己的迭代器或将其他对象调整为迭代器。

为了隐藏迭代器基于<code>std::vector<;弧形*>;:迭代器您需要一个迭代器类来委托std::vector<;弧形*>;:迭代器std::迭代器不执行此操作。

如果您查看编译器C++标准库中的头文件,您可能会发现std::迭代器本身并不是很有用,除非您只需要一个为迭代器类别value_type等定义typedef的类。

正如Doug T.在回答中提到的那样,boost库中的类使编写迭代器变得更容易。特别是,boost::indirect_iterator如果您希望迭代器在取消引用时返回Arc而不是Arc*,则可能会有所帮助。

考虑使用Visitor Pattern并反转关系:您不需要向图结构询问数据容器,而是给图一个函子,让图将该函子应用于其数据。

访问者模式是图形上常用的模式,请查看boost关于访问者概念的图库文档。

如果你真的不想让该类的客户端知道它在下面使用了一个向量,但仍然希望他们能够以某种方式对其进行迭代,那么你很可能需要创建一个类,将其所有方法转发到std::vector::迭代器。

另一种选择是根据Node应该在下面使用的容器类型对其进行模板化。然后客户端特别知道它使用的是什么类型的容器,因为他们告诉他们要使用它。

就我个人而言,我认为封装远离用户的向量通常是没有意义的,但仍然提供了大部分(甚至部分)接口。它的封装层太薄,无法真正提供任何好处。

我查看了头文件VECTOR。

vector<Arc*>::const_iterator

是的typedef

allocator<Arc*>::const_pointer

那会是你的ArcIterator吗?喜欢:

typedef allocator<Arc*>::const_pointer ArcIterator;

您可以将Node类模板化,并在其中对迭代器和const_iterator进行typedef。

例如:

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本身(例如通过非常数方法)。

C++0x将允许您使用自动类型确定

In the new standard, this
for (vector::const_iterator itr = myvec.begin(); itr != myvec.end(); ++itr
can be replaced with this
for (auto itr = myvec.begin(); itr != myvec.end(); ++itr)

同样,您将能够返回任何合适的迭代器,并将其存储在auto变量中。

在新标准生效之前,您必须将类模板化,或者提供一个抽象接口来访问列表/向量的元素。例如,您可以通过在成员变量中存储迭代器来实现这一点,并提供成员函数,如begin()next()。当然,这意味着一次只有一个循环可以安全地迭代您的元素。

因为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;
};

基本上,指针的功能足够像随机访问迭代器,它们是一个足够的替代品。





相关问题
热门标签