代码之家  ›  专栏  ›  技术社区  ›  ereOn

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

  •  200
  • ereOn  · 技术社区  · 14 年前

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

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

    我还想避免代码重复(我觉得 常数迭代器 迭代器 分享很多东西;一个子类应该是另一个子类吗?).

    脚注:我很肯定Boost可以缓解这个问题,但是我不能在这里使用它,因为很多愚蠢的原因。

    6 回复  |  直到 5 年前
        1
  •  137
  •   NathanOliver    9 年前
    • 选择适合容器的迭代器类型:输入、输出、转发等。
    • 使用标准库中的基迭代器类。例如, 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 类型定义:它们是非常量和常量迭代器的类型。

    参见: standard library reference

        2
  •  43
  •   Enzo    6 年前

    我将向您展示如何轻松地为自定义容器定义迭代器,但前提是我已经创建了一个C++ 11库,允许您轻松地为任何类型的容器创建自定义迭代器,这些容器是连续的或非连续的。

    你可以在Github上找到它 https://github.com/navyenzo/blIteratorAPI

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

    1. 创建“自定义迭代器”类。
    2. 在“自定义容器”类中定义typedef。
      • 对于EX: typedef blRawIterator< Type > iterator;
      • 对于EX: typedef blRawIterator< const Type > const_iterator;
    3. 定义“开始”“结束”函数
      • 对于EX: iterator begin(){return iterator(&m_data[0]);};
      • 对于EX: const_iterator cbegin()const{return const_iterator(&m_data[0]);};
    4. 我们完了!!!!

    最后,关于定义自定义迭代器类:

    注: 在定义自定义迭代器时,我们从标准迭代器类别派生,以让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;
    };
    

    祝你好运!!!!

        3
  •  21
  •   Matthieu M.    9 年前

    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
  •  21
  •   Maxim Egorushkin    7 年前

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

        5
  •  16
  •   Potatoswatter R. Martinho Fernandes    14 年前

    我不知道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;
    
        6
  •  4
  •   VinGarcia    7 年前

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

    希望能有所帮助。