如何正确实现自定义迭代器和const_iterators?

时间:2010-08-27 08:50:27

标签: c++ iterator const-iterator

我有一个自定义容器类,我想为其编写iteratorconst_iterator类。

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

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

脚注:我很确定Boost有什么可以缓解的,但我不能在这里使用它,因为很多愚蠢的原因。

8 个答案:

答案 0 :(得分:141)

  • 选择适合您容器的迭代器类型:输入,输出,转发等。
  • 使用标准库中的基础迭代器类。例如,带有random_access_iterator_tag的{​​{3}}。这些基类定义了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迭代器的类型。

另请参阅:std::iterator

答案 1 :(得分:45)

我将向您展示如何轻松地为自定义容器定义迭代器,但以防万一我创建了一个c ++ 11库,允许您轻松创建具有任何类型的自定义行为的自定义迭代器容器,连续或非连续的。

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

的github上找到它

以下是创建和使用自定义迭代器的简单步骤:

  1. 创建您的&#34;自定义迭代器&#34;类。
  2. 在&#34;自定义容器中定义typedef&#34;类。
    • 例如:typedef blRawIterator< Type > iterator;
    • 例如:typedef blRawIterator< const Type > const_iterator;
  3. 定义&#34;开始&#34; &#34;端&#34;功能
    • 例如:iterator begin(){return iterator(&m_data[0]);};
    • 例如:const_iterator cbegin()const{return const_iterator(&m_data[0]);};
  4. 我们已经完成了!!!
  5. 最后,定义我们的自定义迭代器类:

    注意: 在定义自定义迭代器时,我们从标准迭代器类别派生,让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]);}
    
                                .
                                .
                                .
        // This is the pointer to the
        // beginning of the data
        // This allows the container
        // to either "view" data owned
        // by other containers or to
        // own its own data
        // You would implement a "create"
        // method for owning the data
        // and a "wrap" method for viewing
        // data owned by other containers
    
        blDataType*                                    m_data;
    };
    

    好运!!!“

答案 2 :(得分:21)

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

答案 3 :(得分:21)

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

答案 4 :(得分:16)

我不知道Boost是否有任何有用的东西。

我首选的模式很简单:取一个等于value_type的模板参数,或者是否合格。如有必要,还可以是节点类型。然后,好吧,一切都到位了。

请记住参数化(模板化)所有需要的东西,包括复制构造函数和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;

答案 5 :(得分:8)

有很多好的答案,但我创建的template header我使用的非常简洁易用。

要向类添加迭代器,只需编写一个小类来表示迭代器的状态,其中包含7个小函数,其中2个是可选的:

this.props.store.hover

然后您可以像使用STL迭代器那样使用它:

#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);
};

我希望它有所帮助。

答案 6 :(得分:0)

检查下面的代码,它可以正常工作

#define MAX_BYTE_RANGE 255

template <typename T>
class string
{
public:
    typedef char *pointer;
    typedef const char *const_pointer;
    typedef __gnu_cxx::__normal_iterator<pointer, string> iterator;
    typedef __gnu_cxx::__normal_iterator<const_pointer, string> const_iterator;

    string() : length(0)
    {
    }
    size_t size() const
    {
        return length;
    }
    void operator=(const_pointer value)
    {
        if (value == nullptr)
            throw std::invalid_argument("value cannot be null");
        auto count = strlen(value);
        if (count > 0)
            _M_copy(value, count);
    }
    void operator=(const string &value)
    {
        if (value.length != 0)
            _M_copy(value.buf, value.length);
    }
    iterator begin()
    {
        return iterator(buf);
    }
    iterator end()
    {
        return iterator(buf + length);
    }
    const_iterator begin() const
    {
        return const_iterator(buf);
    }
    const_iterator end() const
    {
        return const_iterator(buf + length);
    }
    const_pointer c_str() const
    {
        return buf;
    }
    ~string()
    {
    }

private:
    unsigned char length;
    T buf[MAX_BYTE_RANGE];

    void _M_copy(const_pointer value, size_t count)
    {
        memcpy(buf, value, count);
        length = count;
    }
};

答案 7 :(得分:0)

我看到了这篇文章,很惊讶这里没有真正提到一个简单的方法。像 std::iterator 描述的那样使用指向值的指针显然是一种非常通用的方法。但是你也许可以通过一些更简单的东西来逃脱。当然,这是一种简单的方法,可能并不总是足够的,但如果是,我会将其发布给下一位读者。

你的类中的底层类型很可能是一个 STL 容器,它已经为你定义了迭代器。如果是这种情况,您可以简单地使用他们定义的迭代器,而不需要自己制作。

这是一个例子:

class Foo {

  std::vector<int>::iterator begin() { return data.begin(); }
  std::vector<int>::iterator end() { return data.end(); }

  std::vector<int>::const_iterator begin() const { return data.begin(); }
  std::vector<int>::const_iterator end() const { return data.end(); }


private:
  std::vector<int> data

};