Как правильно реализовать пользовательские итераторы и итераторы const?
у меня есть пользовательский класс контейнера, для которого я хотел бы написать iterator
и const_iterator
классы.
Я никогда не делал этого раньше, и я не смог найти подходящий способ. Каковы рекомендации по созданию итератора, и что я должен знать ?
Я также хотел бы избежать дублирования кода (я чувствую, что const_iterator
и iterator
поделиться многими вещами; должен ли один подкласс другой ?).
Примечание для ног: я уверен, что Boost имеет что-то облегчите это, но я не могу использовать его здесь, по многим глупым причинам.
6 ответов:
- выберите тип итератора, который соответствует вашему контейнеру: вход, выход, вперед и т. д.
- используйте базовые классы итераторов из стандартной библиотеки. Например,
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
определения типов: это типы для ваших неконстантных и константных итераторов.Читайте Также: стандартная ссылка на библиотеку
Я покажу вам, как вы можете легко определить итераторы для своих пользовательских контейнеров, но на всякий случай я создал библиотеку c++11, которая позволяет легко создавать пользовательские итераторы с пользовательским поведением для любого типа контейнера, смежного или несмежного.
вы можете найти его на github в https://github.com/navyenzo/blIteratorAPI
вот простые шаги для создания и использования пользовательских итераторов:
- создать ваш класс "пользовательский итератор".
- определите 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]);};
- мы закончили!!!
наконец, при определении наших пользовательских классов итераторов:
Примечание: при определении пользовательских итераторов мы выводим из стандартных категорий итераторов, чтобы алгоритмы 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; };
УДАЧИ!!!
Boost есть что-то, чтобы помочь: повышение.Библиотека итераторов.
точнее эту страницу: 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
его.
они часто забывают об этом
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 что-нибудь, что поможет.
мой предпочтительный шаблон прост: возьмите аргумент шаблона, который равен
value_type
, либо const квалифицированный, либо нет. При необходимости, также тип узла. Тогда, ну, все вроде как встает на свои места.просто не забудьте параметризовать (template-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;
есть много хороших ответов, но у меня заголовок шаблона Я использую, что является довольно кратким и простым в использовании.
чтобы добавить итератор в свой класс, необходимо только написать небольшой класс для представления состояния итератора с 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 } }
Я надеюсь, что это помогает.