簡體   English   中英

如何正確實現自定義迭代器和 const_iterators?

[英]How to correctly implement custom iterators and const_iterators?

我有一個自定義容器類,我想為其編寫iteratorconst_iterator類。

我以前從未這樣做過,也沒有找到合適的操作方法。 關於迭代器創建的指導方針是什么,我應該注意什么?

我還想避免代碼重復(我覺得const_iteratoriterator共享很多東西;應該一個子類化另一個嗎?)。

腳注:我很確定 Boost 有一些東西可以緩解這個問題,但由於許多愚蠢的原因,我不能在這里使用它。

  • 選擇適合您的容器的迭代器類型:輸入、輸出、轉發等。
  • 使用標准庫中的基迭代器類。 例如,帶有random_access_iterator_tag std::iterator 。這些基類定義了 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類型定義:它們是非常量和常量迭代器的類型。

另見:標准庫參考

編輯: std::iterator自 C++17 起已被棄用。 請參閱此處的相關討論。

我將向您展示如何輕松地為您的自定義容器定義迭代器,但以防萬一我創建了一個 c++11 庫,它允許您輕松地為任何類型的容器創建具有自定義行為的自定義迭代器,連續或不連續。

你可以在 Github 上找到

以下是創建和使用自定義迭代器的簡單步驟:

  1. 創建您的“自定義迭代器”類。
  2. 在“自定義容器”類中定義 typedef。
    • 例如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. 我們完成了!!!

最后,定義我們的自定義迭代器類:

注意:在定義自定義迭代器時,我們從標准迭代器類別派生,讓 STL 算法知道我們創建的迭代器的類型。

在這個例子中,我定義了一個隨機訪問迭代器和一個反向隨機訪問迭代器:

  1.  //------------------------------------------------------------------- // Raw iterator with random access //------------------------------------------------------------------- template<typename blDataType> class blRawIterator { public: using iterator_category = std::random_access_iterator_tag; using value_type = blDataType; using difference_type = std::ptrdiff_t; using pointer = blDataType*; using reference = 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 difference_type& movement){m_ptr += movement;return (*this);} blRawIterator<blDataType>& operator-=(const difference_type& movement){m_ptr -= movement;return (*this);} blRawIterator<blDataType>& operator++(){++m_ptr;return (*this);} blRawIterator<blDataType>& operator--(){--m_ptr;return (*this);} blRawIterator<blDataType> operator++(int){auto temp(*this);++m_ptr;return temp;} blRawIterator<blDataType> operator--(int){auto temp(*this);--m_ptr;return temp;} blRawIterator<blDataType> operator+(const difference_type& movement){auto oldPtr = m_ptr;m_ptr+=movement;auto temp(*this);m_ptr = oldPtr;return temp;} blRawIterator<blDataType> operator-(const difference_type& movement){auto oldPtr = m_ptr;m_ptr-=movement;auto temp(*this);m_ptr = oldPtr;return temp;} difference_type 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 difference_type& movement){this->m_ptr -= movement;return (*this);} blRawReverseIterator<blDataType>& operator-=(const difference_type& 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++(int){auto temp(*this);--this->m_ptr;return temp;} blRawReverseIterator<blDataType> operator--(int){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;} difference_type 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;
};

他們經常忘記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這個轉換永遠不會被使用。

Boost 有一些幫助:Boost.Iterator 庫。

更准確地說,這個頁面: boost::iterator_adaptor

非常有趣的是教程示例,它顯示了自定義類型的完整實現,從頭開始。

 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

我不知道 Boost 是否有任何幫助。

我的首選模式很簡單:取一個等於value_type的模板參數,無論是否為 const 限定。 如有必要,還有一個節點類型。 然后,好吧,一切都到位了。

只要記住參數化(模板化)所有需要的東西,包括復制構造函數和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;

有很多好的答案,但我創建了一個模板標題,我使用它非常簡潔且易於使用。

要將迭代器添加到您的類中,只需編寫一個小類來表示具有 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
  }
}

我希望它有幫助。

我看到了這篇文章,很驚訝這里沒有真正提到一個簡單的方法。 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

};

我很想知道這是多么正確,但似乎可以作為內部數據存儲的滾動迭代器

template<typename T>
struct iterator_type
{
    using self_type         = iterator_type;
    using iterator_category = std::forward_iterator_tag;
    using difference_type   = std::ptrdiff_t;
    using value_type        = T;
    using pointer           = value_type*;
    using reference         = value_type&;

    iterator_type( pointer ptr ) noexcept
        : _ptr{ ptr }
    {}

    reference operator*() noexcept { return *_ptr; }
    pointer operator->() noexcept { return _ptr; }

    self_type operator++() noexcept { _ptr++; return *this; }
    self_type operator++(int) noexcept { self_type tmp = *this; _ptr++; return tmp; }

    self_type operator--() noexcept { _ptr--; return *this; }
    self_type operator--(int) noexcept { self_type tmp = *this; _ptr--; return tmp; }

    bool operator==( const self_type &other ) const noexcept { return _ptr == other._ptr; }
    bool operator!=( const self_type &other ) const noexcept { return !(*this == other); }
    
private:
    pointer _ptr;
};


template<typename T>
using const_iterator_type = iterator_type<std::add_const_t<T>>;

然后我只是將這些添加到我的班級中,並且似乎按預期工作。

    using iterator               = iterator_type<T>;
    using const_iterator         = const_iterator_type<T>;
    using reverse_iterator       = std::reverse_iterator<iterator>;
    using const_reverse_iterator = std::reverse_iterator<const_iterator>;

    iterator begin() { return _begin; }
    iterator end() { return _begin + _size; }

    const_iterator cbegin() const { return _begin; }
    const_iterator cend() const { return _begin + _size; }

    reverse_iterator rbegin() { return reverse_iterator(_begin + _size); }
    reverse_iterator rend() { return reverse_iterator(_begin); }

    const_reverse_iterator crbegin() const { return const_reverse_iterator(_begin + _size); }
    const_reverse_iterator crend() const { return const_reverse_iterator(_begin); }

private:
    T *_begin;
    size_t _size;
    size_t _capacity;

是唯一的事情就是不與不工作std::cbegin() std::rcbegin() std::cend()std::rcend()函數。 其他一切似乎都很好。

檢查下面的代碼,它有效

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM