Je vais vous montrer comment vous pouvez facilement définir des itérateurs pour vos conteneurs personnalisés, mais juste au cas où, j'ai créé une bibliothèque c++11 qui vous permet de créer facilement des itérateurs personnalisés avec un comportement personnalisé pour tout type de conteneur, contigu ou non contigu.
Vous pouvez le trouver sur Github
Voici les étapes simples pour créer et utiliser des itérateurs personnalisés :
- Créez votre classe "itérateur personnalisé".
- Définissez les typedefs dans votre classe "custom container".
- par exemple
typedef blRawIterator< Type > iterator;
- par exemple
typedef blRawIterator< const Type > const_iterator;
- Définir les fonctions "début" et "fin".
- par exemple
iterator begin(){return iterator(&m_data[0]);};
- par exemple
const_iterator cbegin()const{return const_iterator(&m_data[0]);};
- C'est fait ! !!
Enfin, nous allons définir nos classes d'itérateurs personnalisés :
NOTE : Lorsque nous définissons des itérateurs personnalisés, nous dérivons des catégories d'itérateurs standard pour permettre aux algorithmes de la STL de connaître le type d'itérateur que nous avons créé.
Dans cet exemple, je définis un itérateur d'accès aléatoire et un itérateur d'accès aléatoire inverse :
-
//-------------------------------------------------------------------
// 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;
};
//-------------------------------------------------------------------
-
//-------------------------------------------------------------------
// 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;}
};
//-------------------------------------------------------------------
Maintenant, quelque part dans votre classe de conteneur personnalisée :
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 votes
De l'ensemble des questions connexes : Le type 'iterator' peut-il simplement sous-classer 'const_iterator' ? Comment éviter la duplication du code lors de la mise en œuvre d'itérateurs constants et non constants ?
0 votes
Le modèle d'itérateur GoF est-il envisagé ?
4 votes
@DumbCoder : En C++, il est souvent souhaitable d'avoir des itérateurs qui sont conformes à la STL, parce qu'ils fonctionneront bien avec tous les conteneurs et algorithmes existants fournis par la STL. Bien que le concept soit similaire, il y a quelques différences avec le modèle proposé par le GoF.
0 votes
J'ai posté un exemple d'itérateur personnalisé. aquí
1 votes
La complexité de ces réponses suggère que le C++ est soit un langage indigne d'autre chose que des devoirs pour des étudiants de premier cycle, soit que les réponses sont trop compliquées et fausses. Il doit y avoir un moyen plus facile en Cpp ? Comme CMake et Automake avant lui par rapport à make, le C brut sorti d'un prototype python semble beaucoup plus facile que cela.
0 votes
J'ai récemment publié un article sur ce sujet : C++ Senioreas - Personnalisation des itérateurs
1 votes
@Chris Le C++ est certainement digne d'intérêt. La complexité du C++ et la courbe d'apprentissage sont en quelque sorte un paiement pour ses attributs relativement uniques d'abstractions extrêmement optimisées. Certains parlent d'abstractions à coût zéro et dans de nombreux cas, c'est exactement ce qui se passe dans le C++ moderne.
2 votes
@PavelŠimerda haha Ouais, je ne suis pas du tout d'accord avec mon commentaire à ce stade en général. Je pense que je pensais plutôt à "il doit y avoir un moyen plus facile".