2010-08-27 106 views
154

我有一个自定义容器类,我想写iteratorconst_iterator类。如何正确实现自定义迭代器和const_iterators?

我从来没有这样做过,我没有找到合适的方法。关于迭代器创建的指导原则是什么?我应该注意什么?

我也想避免代码重复(我觉得const_iteratoriterator分享很多东西;如果一个子类是另一个?)。

注脚:我敢肯定,加速了东西来缓解这一点,但我不能在这里使用它,对于许多愚蠢的理由。

+1

从相关问题的设置:[可'迭代器'类只是子类'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

+0

GoF迭代器模式是否被考虑? – DumbCoder 2010-08-27 09:02:55

+3

@DumbCoder:在C++中,通常需要使用符合STL的迭代器,因为它们可以很好地与STL提供的所有现有容器和算法配合使用。虽然概念相似,但GoF提出的模式存在一些差异。 – 2010-08-27 09:51:28

回答

124
  • 选择迭代其适合你容器的类型:输入,输出,从向前标准库等
  • 使用基本的迭代器类。例如,std::iteratorrandom_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_typeconst_iterator_type类型定义:他们是类型的非const和const迭代器。

另请参见:standard library reference

+0

@Andrey你能解释值类型,指针类型和引用类型之间的区别吗? – radicalmatt 2012-07-26 19:52:25

+8

@Patatoswatter:没有downvoted这个,但是,嘿,'random_access_iterator'不在标准中,答案没有处理可变的const转换。您可能想要继承,例如'std :: iterator '虽然。 – ybungalobill 2012-10-08 14:55:08

+2

是的,我不太清楚这是如何工作的。如果我有'RefType operator *(){...}'方法,我更靠近一步 - 但它没有帮助,因为我仍然需要'RefType operator *()const {...} 。 – 2013-09-05 05:42:58

16

我不知道,如果加速有什么关系,这将有助于。

我的优选图案是简单的:取的模板的参数,它等于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; 
+0

我从来不知道这很简单。谢谢。 – ereOn 2010-08-27 09:39:04

+0

注意:它看起来像你的转换iterator-> const_iterator和后退是坏的。 – 2010-08-27 09:41:24

+0

@Maxim:是的,我实际上找不到使用我的技术的任何例子:vP。我不确定你的意思是转换是否被破坏,因为我只是没有说明它们,但是从相反常量的迭代器访问'cur'可能存在问题。想到的解决方案是'friend my_container :: const_iterator;朋友my_container :: iterator;',但我不认为这就是我之前做过的...无论如何,这个总体大纲的作品。 – Potatoswatter 2010-08-27 09:52:31

18

他们往往忘记了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,则此转换永远不会被使用。

+0

实际上,您也可以通过定义复制构造函数模板,如果您尝试将基础类型从'const'转换为非''constst',它将不会编译。 – 2010-08-27 09:48:28

+0

你最终不会得到一个无效的'IntrusiveSlistIterator :: operator IntrusiveSlistIterator ()const'吗? – Potatoswatter 2010-08-27 09:56:40

+0

啊,它是有效的,但Comeau发出警告,我怀疑还有很多其他人。一个'enable_if'可能会修复它,但... – Potatoswatter 2010-08-27 09:59:43

19

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它。

+0

+1绝对是唯一的选择。 – Sebastian 2010-08-27 13:25:23

+0

你能解释一下这个评论的含义吗? ''//私有类型避免误用 ' – kevinarpe 2017-02-07 06:39:13

+0

@kevinarpe:'enabler'从来不打算成为调用者的提供者,所以我的猜测是他们使得它是私人的,以避免人们意外地尝试传递它。我并不认为它可能会产生任何问题来实际传递它,因为保护位于'enable_if'中。 – 2017-02-07 07:25:54

29

我将向您展示如何为您的自定义容器轻松定义迭代器,但以防万一我创建了一个C++ 11库,使您可以轻松创建具有自定义行为的自定义迭代器,以适应任何类型的集装箱,连续或非连续的。

您可以在https://github.com/navyenzo/blIteratorAPI

发现它在github下面是简单的步骤来创建和使用自定义的迭代器:

  1. 创建“自定义迭代”级。
  2. 在“自定义容器”类中定义typedefs。
    • 对于前:typedef blRawIterator< Type > iterator;
    • 对于前:typedef blRawIterator< const Type > const_iterator;
  3. 定义 “开始”, “结束” 功能
    • 对于前:iterator begin(){return iterator(&m_data[0]);};
    • 对于前:const_iterator cbegin()const{return const_iterator(&m_data[0]);};
  4. 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!

+0

我认为运营商+和运营商可能会倒退。它看起来像operator +正在减去指针不加的移动,而operator-正在增加它。这似乎是倒退 – Beached 2017-06-18 01:13:55

+0

这是反向迭代器,运营商+应该倒退和运营商 - 应该前进 – Enzo 2017-09-06 18:39:36

+0

真棒。接受的答案太高。这太棒了。谢谢恩佐。 – FernandoZ 2017-11-19 21:11:51

0

有很多很好的答案,但我有一个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 
    } 
} 

我希望它有帮助。