Как правильно реализовать пользовательские итераторы и итераторы const?
у меня есть пользовательский класс контейнера, для которого я хотел бы написать iterator
и const_iterator
классы.
Я никогда не делал этого раньше, и мне не удалось найти подходящую инструкции. Каковы рекомендации по созданию итератора и что я должен знать ?
Я также хотел бы избежать дублирования кода (я чувствую, что const_iterator
и iterator
поделитесь многими вещами; должен ли один подкласс другой ?).
Foot примечание: Я уверен, что 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
определения типов: это типы для итераторов non-const и const.
Читайте Также: ссылка на стандартную библиотеку
Я покажу вам, как вы можете легко определить итераторы для своих пользовательских контейнеров, но на всякий случай я создал библиотеку c++11, которая позволяет легко создавать пользовательские итераторы с пользовательским поведением для любого типа контейнера, смежного или несмежного.
вы можете найти его на github в https://github.com/navyenzo/blIteratorAPI
вот простые шаги по созданию и использованию пользовательских итераторов:
- создать ваш класс "пользовательский итератор".
- определите typedefs в своем классе "пользовательский контейнер".
- например:
typedef blRawIterator< Type > iterator;
- например:
typedef blRawIterator< const Type > const_iterator;
- например:
- определите функции" begin "" end"
- например:
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
}
}
надеюсь, это поможет.