如何正确实现自定义迭代器和const_iterators?
我有一个自定义容器类,我想写iterator
和const_iterator
类。如何正确实现自定义迭代器和const_iterators?
我从来没有这样做过,我没有找到合适的方法。关于迭代器创建的指导原则是什么?我应该注意什么?
我也想避免代码重复(我觉得const_iterator
和iterator
分享很多东西;如果一个子类是另一个?)。
注脚:我敢肯定,加速了东西来缓解这一点,但我不能在这里使用它,对于许多愚蠢的理由。
- 选择迭代其适合你容器的类型:输入,输出,从向前标准库等
- 使用基本的迭代器类。例如,
std::iterator
与random_access_iterator_tag
。这些基类定义了STL所需的所有类型定义并执行其他工作。 -
为了避免重复代码的迭代器类应该是一个模板类,并通过“值类型”,“指针”,“引用类型”或所有的(取决于实现)被参数。例如:
// iterator class is parametrized by pointer type template <typename PointerType> class MyIterator { // iterator class definition goes here }; typedef MyIterator<int*> iterator_type; typedef MyIterator<const int*> const_iterator_type;
通知
iterator_type
和const_iterator_type
类型定义:他们是类型的非const和const迭代器。
@Andrey你能解释值类型,指针类型和引用类型之间的区别吗? – radicalmatt 2012-07-26 19:52:25
@Patatoswatter:没有downvoted这个,但是,嘿,'random_access_iterator'不在标准中,答案没有处理可变的const转换。您可能想要继承,例如'std :: iterator
是的,我不太清楚这是如何工作的。如果我有'RefType operator *(){...}'方法,我更靠近一步 - 但它没有帮助,因为我仍然需要'RefType operator *()const {...} 。 – 2013-09-05 05:42:58
我不知道,如果加速有什么关系,这将有助于。
我的优选图案是简单的:取的模板的参数,它等于value_type
,无论是常量合格与否。如有必要,也是一种节点类型。那么,好吧,一切都会落到实处。
只要记住参数(模板IZE)需要进行,包括拷贝构造函数和operator==
一切。在大多数情况下,const
的语义会创建正确的行为。
template< class ValueType, class NodeType >
struct my_iterator
: std::iterator< std::bidirectional_iterator_tag, T > {
ValueType &operator*() { return cur->payload; }
template< class VT2, class NT2 >
friend bool operator==
(my_iterator const &lhs, my_iterator< VT2, NT2 > const &rhs);
// etc.
private:
NodeType *cur;
friend class my_container;
my_iterator(NodeType *); // private constructor for begin, end
};
typedef my_iterator< T, my_node<T> > iterator;
typedef my_iterator< T const, my_node<T> const > const_iterator;
我从来不知道这很简单。谢谢。 – ereOn 2010-08-27 09:39:04
注意:它看起来像你的转换iterator-> const_iterator和后退是坏的。 – 2010-08-27 09:41:24
@Maxim:是的,我实际上找不到使用我的技术的任何例子:vP。我不确定你的意思是转换是否被破坏,因为我只是没有说明它们,但是从相反常量的迭代器访问'cur'可能存在问题。想到的解决方案是'friend my_container :: const_iterator;朋友my_container :: iterator;',但我不认为这就是我之前做过的...无论如何,这个总体大纲的作品。 – Potatoswatter 2010-08-27 09:52:31
他们往往忘记了iterator
必须转换为const_iterator
而不是周围的其他方式。这里是一个办法做到这一点:
template<class T, class Tag = void>
class IntrusiveSlistIterator
: public std::iterator<std::forward_iterator_tag, T>
{
typedef SlistNode<Tag> Node;
Node* node_;
public:
IntrusiveSlistIterator(Node* node);
T& operator*() const;
T* operator->() const;
IntrusiveSlistIterator& operator++();
IntrusiveSlistIterator operator++(int);
friend bool operator==(IntrusiveSlistIterator a, IntrusiveSlistIterator b);
friend bool operator!=(IntrusiveSlistIterator a, IntrusiveSlistIterator b);
// one way conversion: iterator -> const_iterator
operator IntrusiveSlistIterator<T const, Tag>() const;
};
在上述公告如何IntrusiveSlistIterator<T>
转换为IntrusiveSlistIterator<T const>
。如果T
已经为const
,则此转换永远不会被使用。
实际上,您也可以通过定义复制构造函数模板,如果您尝试将基础类型从'const'转换为非''constst',它将不会编译。 – 2010-08-27 09:48:28
你最终不会得到一个无效的'IntrusiveSlistIterator
啊,它是有效的,但Comeau发出警告,我怀疑还有很多其他人。一个'enable_if'可能会修复它,但... – Potatoswatter 2010-08-27 09:59:43
Boost有一些帮助:Boost.Iterator库。
更确切的这个页面:boost::iterator_adaptor。
什么是非常有趣的是Tutorial Example它显示了一个完整的实现,从无到有,自定义类型。
template <class Value> class node_iter : public boost::iterator_adaptor< node_iter<Value> // Derived , Value* // Base , boost::use_default // Value , boost::forward_traversal_tag // CategoryOrTraversal > { private: struct enabler {}; // a private type avoids misuse public: node_iter() : node_iter::iterator_adaptor_(0) {} explicit node_iter(Value* p) : node_iter::iterator_adaptor_(p) {} // iterator convertible to const_iterator, not vice-versa template <class OtherValue> node_iter( node_iter<OtherValue> const& other , typename boost::enable_if< boost::is_convertible<OtherValue*,Value*> , enabler >::type = enabler() ) : node_iter::iterator_adaptor_(other.base()) {} private: friend class boost::iterator_core_access; void increment() { this->base_reference() = this->base()->next(); } };
主要的一点,因为已经被引用,就是用一个模板的实施和typedef
它。
我将向您展示如何为您的自定义容器轻松定义迭代器,但以防万一我创建了一个C++ 11库,使您可以轻松创建具有自定义行为的自定义迭代器,以适应任何类型的集装箱,连续或非连续的。
您可以在https://github.com/navyenzo/blIteratorAPI
发现它在github下面是简单的步骤来创建和使用自定义的迭代器:
- 创建“自定义迭代”级。
- 在“自定义容器”类中定义typedefs。
- 对于前:
typedef blRawIterator< Type > iterator;
- 对于前:
typedef blRawIterator< const Type > const_iterator;
- 对于前:
- 定义 “开始”, “结束” 功能
- 对于前:
iterator begin(){return iterator(&m_data[0]);};
- 对于前:
const_iterator cbegin()const{return const_iterator(&m_data[0]);};
- 对于前:
- We'r e完成!
最后,在确定我们自定义的迭代器类:
注:当定义自定义迭代器,我们从标准的迭代器分类导出,让STL算法知道我们已经取得迭代器的类型
在本例中,我定义一个随机访问迭代和反向随机访问迭代:
1.
//-------------------------------------------------------------------
// Raw iterator with random access
//-------------------------------------------------------------------
template<typename blDataType>
class blRawIterator : public std::iterator<std::random_access_iterator_tag,
blDataType,
ptrdiff_t,
blDataType*,
blDataType&>
{
public:
blRawIterator(blDataType* ptr = nullptr){m_ptr = ptr;}
blRawIterator(const blRawIterator<blDataType>& rawIterator) = default;
~blRawIterator(){}
blRawIterator<blDataType>& operator=(const blRawIterator<blDataType>& rawIterator) = default;
blRawIterator<blDataType>& operator=(blDataType* ptr){m_ptr = ptr;return (*this);}
operator bool()const
{
if(m_ptr)
return true;
else
return false;
}
bool operator==(const blRawIterator<blDataType>& rawIterator)const{return (m_ptr == rawIterator.getConstPtr());}
bool operator!=(const blRawIterator<blDataType>& rawIterator)const{return (m_ptr != rawIterator.getConstPtr());}
blRawIterator<blDataType>& operator+=(const ptrdiff_t& movement){m_ptr += movement;return (*this);}
blRawIterator<blDataType>& operator-=(const ptrdiff_t& movement){m_ptr -= movement;return (*this);}
blRawIterator<blDataType>& operator++(){++m_ptr;return (*this);}
blRawIterator<blDataType>& operator--(){--m_ptr;return (*this);}
blRawIterator<blDataType> operator++(ptrdiff_t){auto temp(*this);++m_ptr;return temp;}
blRawIterator<blDataType> operator--(ptrdiff_t){auto temp(*this);--m_ptr;return temp;}
blRawIterator<blDataType> operator+(const ptrdiff_t& movement){auto oldPtr = m_ptr;m_ptr+=movement;auto temp(*this);m_ptr = oldPtr;return temp;}
blRawIterator<blDataType> operator-(const ptrdiff_t& movement){auto oldPtr = m_ptr;m_ptr-=movement;auto temp(*this);m_ptr = oldPtr;return temp;}
ptrdiff_t operator-(const blRawIterator<blDataType>& rawIterator){return std::distance(rawIterator.getPtr(),this->getPtr());}
blDataType& operator*(){return *m_ptr;}
const blDataType& operator*()const{return *m_ptr;}
blDataType* operator->(){return m_ptr;}
blDataType* getPtr()const{return m_ptr;}
const blDataType* getConstPtr()const{return m_ptr;}
protected:
blDataType* m_ptr;
};
//-------------------------------------------------------------------
2.
//-------------------------------------------------------------------
// Raw reverse iterator with random access
//-------------------------------------------------------------------
template<typename blDataType>
class blRawReverseIterator : public blRawIterator<blDataType>
{
public:
blRawReverseIterator(blDataType* ptr = nullptr):blRawIterator<blDataType>(ptr){}
blRawReverseIterator(const blRawIterator<blDataType>& rawIterator){this->m_ptr = rawIterator.getPtr();}
blRawReverseIterator(const blRawReverseIterator<blDataType>& rawReverseIterator) = default;
~blRawReverseIterator(){}
blRawReverseIterator<blDataType>& operator=(const blRawReverseIterator<blDataType>& rawReverseIterator) = default;
blRawReverseIterator<blDataType>& operator=(const blRawIterator<blDataType>& rawIterator){this->m_ptr = rawIterator.getPtr();return (*this);}
blRawReverseIterator<blDataType>& operator=(blDataType* ptr){this->setPtr(ptr);return (*this);}
blRawReverseIterator<blDataType>& operator+=(const ptrdiff_t& movement){this->m_ptr -= movement;return (*this);}
blRawReverseIterator<blDataType>& operator-=(const ptrdiff_t& movement){this->m_ptr += movement;return (*this);}
blRawReverseIterator<blDataType>& operator++(){--this->m_ptr;return (*this);}
blRawReverseIterator<blDataType>& operator--(){++this->m_ptr;return (*this);}
blRawReverseIterator<blDataType> operator++(ptrdiff_t){auto temp(*this);--this->m_ptr;return temp;}
blRawReverseIterator<blDataType> operator--(ptrdiff_t){auto temp(*this);++this->m_ptr;return temp;}
blRawReverseIterator<blDataType> operator+(const int& movement){auto oldPtr = this->m_ptr;this->m_ptr-=movement;auto temp(*this);this->m_ptr = oldPtr;return temp;}
blRawReverseIterator<blDataType> operator-(const int& movement){auto oldPtr = this->m_ptr;this->m_ptr+=movement;auto temp(*this);this->m_ptr = oldPtr;return temp;}
ptrdiff_t operator-(const blRawReverseIterator<blDataType>& rawReverseIterator){return std::distance(this->getPtr(),rawReverseIterator.getPtr());}
blRawIterator<blDataType> base(){blRawIterator<blDataType> forwardIterator(this->m_ptr); ++forwardIterator; return forwardIterator;}
};
//-------------------------------------------------------------------
现在,在您的自定义容器类的地方:
template<typename blDataType>
class blCustomContainer
{
public: // The typedefs
typedef blRawIterator<blDataType> iterator;
typedef blRawIterator<const blDataType> const_iterator;
typedef blRawReverseIterator<blDataType> reverse_iterator;
typedef blRawReverseIterator<const blDataType> const_reverse_iterator;
.
.
.
public: // The begin/end functions
iterator begin(){return iterator(&m_data[0]);}
iterator end(){return iterator(&m_data[m_size]);}
const_iterator cbegin(){return const_iterator(&m_data[0]);}
const_iterator cend(){return const_iterator(&m_data[m_size]);}
reverse_iterator rbegin(){return reverse_iterator(&m_data[m_size - 1]);}
reverse_iterator rend(){return reverse_iterator(&m_data[-1]);}
const_reverse_iterator crbegin(){return const_reverse_iterator(&m_data[m_size - 1]);}
const_reverse_iterator crend(){return const_reverse_iterator(&m_data[-1]);}
.
.
.
};
GOOD LUCK!
有很多很好的答案,但我有一个template header我使用它是相当简洁和易于使用。
要迭代器添加到您的类,只需要编写一个小的类来表示迭代的状态,7个个小功能,其中2个是可选的:
#include <iostream>
#include <vector>
#include "iterator_tpl.h"
struct myClass {
std::vector<float> vec;
// Add some sane typedefs for STL compliance:
STL_TYPEDEFS(float);
struct it_state {
int pos;
inline void begin(const myClass* ref) { pos = 0; }
inline void next(const myClass* ref) { ++pos; }
inline void end(const myClass* ref) { pos = ref->vec.size(); }
inline float& get(myClass* ref) { return ref->vec[pos]; }
inline bool cmp(const it_state& s) const { return pos != s.pos; }
// Optional to allow operator--() and reverse iterators:
inline void prev(const myClass* ref) { --pos; }
// Optional to allow `const_iterator`:
inline const float& get(const myClass* ref) const { return ref->vec[pos]; }
};
// Declare typedef ... iterator;, begin() and end() functions:
SETUP_ITERATORS(myClass, float&, it_state);
// Declare typedef ... reverse_iterator;, rbegin() and rend() functions:
SETUP_REVERSE_ITERATORS(myClass, float&, it_state);
};
然后你可以使用它如你所期望的STL迭代器:
int main() {
myClass c1;
c1.vec.push_back(1.0);
c1.vec.push_back(2.0);
c1.vec.push_back(3.0);
std::cout << "iterator:" << std::endl;
for (float& val : c1) {
std::cout << val << " "; // 1.0 2.0 3.0
}
std::cout << "reverse iterator:" << std::endl;
for (auto it = c1.rbegin(); it != c1.rend(); ++it) {
std::cout << *it << " "; // 3.0 2.0 1.0
}
}
我希望它有帮助。
从相关问题的设置:[可'迭代器'类只是子类'const_iterator'?](http://stackoverflow.com/questions/2844466/can-iterator-type-just-subclass-const-iterator) [在实现const和非const迭代器时,你如何避免代码重复?](http://stackoverflow.com/questions/2150192/how-do-you-avoid-code-duplication-when-implementing-const-and-非常量迭代器) – sbi 2010-08-27 08:54:14
GoF迭代器模式是否被考虑? – DumbCoder 2010-08-27 09:02:55
@DumbCoder:在C++中,通常需要使用符合STL的迭代器,因为它们可以很好地与STL提供的所有现有容器和算法配合使用。虽然概念相似,但GoF提出的模式存在一些差异。 – 2010-08-27 09:51:28