假设我想在C ++中实现一个数据结构来存储面向图。由于STL容器,Arcs将存储在节点中。我希望用户能够以类似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可以是什么?
我发现这篇论文关于Custom Iterators for the STL,但它没有帮助。今天我一定有点沉重......;)
答案 0 :(得分:8)
试试这个:
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。
答案 1 :(得分:3)
查看Adobe的any_iterator
:此类使用一种名为 type erase 的技术,通过该技术,底层迭代器类型隐藏在抽象接口后面。注意:由于虚拟调度,使用any_iterator
会导致运行时损失。
答案 2 :(得分:1)
我想认为应该有一种方法可以通过直接STL来做到这一点,类似于你想要做的事情。
如果没有,您可能希望使用boost's iterator facades and adaptors来定义自己的迭代器或将其他对象调整为迭代器。
答案 3 :(得分:1)
要隐藏迭代器基于std::vector<Arc*>::iterator
的事实,您需要一个委托给std::vector<Arc*>::iterator
的迭代器类。 std::iterator
不会这样做。
如果查看编译器的C ++标准库中的头文件,您可能会发现std::iterator
本身并不是很有用,除非您只需要为{{1}定义typedef的类},iterator_category
等
正如Doug T.在他的回答中提到的,boost库有一些类,可以更容易地编写迭代器。特别是,如果您希望迭代器在取消引用而不是value_type
时返回Arc
,那么boost::indirect_iterator
可能会有所帮助。
答案 4 :(得分:1)
考虑使用Visitor Pattern并反转关系:不是向图表结构询问数据容器,而是将图形作为仿函数,让图形将该仿函数应用于其数据。
访问者模式是图表上常用的模式,请查看关于访问者概念的boost图表库文档。
答案 5 :(得分:0)
如果你真的不希望该类的客户端知道它使用了下面的向量,但仍然希望它们能够以某种方式迭代它,那么你很可能需要创建一个转发所有它的类std :: vector :: iterator。
的方法另一种方法是根据Node下面应该使用的容器类型来模拟Node。然后客户特别知道它正在使用什么类型的容器,因为他们告诉他们使用它。
就我个人而言,我认为将矢量封装远离用户通常是有意义的,但仍然提供其大部分(甚至一些)接口。它太薄了封装层,无法真正提供任何好处。
答案 6 :(得分:0)
我查看了头文件VECTOR。
vector<Arc*>::const_iterator
是
的typedefallocator<Arc*>::const_pointer
这可能是你的ArcIterator吗?像:
typedef allocator<Arc*>::const_pointer ArcIterator;
答案 7 :(得分:0)
你可以对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方法)。
答案 8 :(得分:0)
C ++ 0x允许您使用automatic type determination执行此操作。
在新标准中,这个
for (vector::const_iterator itr = myvec.begin(); itr != myvec.end(); ++itr
可以用这个替换
for (auto itr = myvec.begin(); itr != myvec.end(); ++itr)
出于同样的原因,您将能够返回任何适当的迭代器,并将其存储在auto
变量中。
在新标准出现之前,您必须对您的类进行模板化,或者提供抽象接口来访问列表/向量的元素。例如,您可以通过在成员变量中存储迭代器来实现,并提供成员函数,如begin()
和next()
。当然,这意味着一次只能有一个循环可以安全地迭代你的元素。
答案 9 :(得分:0)
因为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;
};
基本上,指针的功能就像随机访问迭代器一样,它们是足够的替代品。