사용자 정의 반복자와 const_iterator를 올바르게 구현하는 방법은 무엇입니까?
iterator
and const_iterator
클래스 를 작성하려는 사용자 정의 컨테이너 클래스가 있습니다.
나는 전에 이것을 한 적이 없으며 적절한 방법을 찾지 못했습니다. 반복자 생성에 관한 지침은 무엇이며 무엇을 알고 있어야합니까?
또한 피하기 코드 중복을 같은 (나는 것을 느낄 것 const_iterator
하고 iterator
, 하나가 다른 서브 클래스해야 공유 많은 것들?).
각주 : Boost는 이것을 쉽게 할 수있는 것이 확실하지만 많은 어리석은 이유로 여기에서는 사용할 수 없습니다.
- 컨테이너에 맞는 반복기 유형 (입력, 출력, 전달 등)을 선택하십시오.
- 표준 라이브러리에서 기본 반복기 클래스를 사용하십시오. 예를 들어,
std::iterator
함께random_access_iterator_tag
.These 기본 클래스는 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
유형 정의 : 비 const 및 const 반복자에 대한 유형입니다.
참조 : 표준 라이브러리 참조
편집 : std::iterator
C ++ 17부터 더 이상 사용되지 않습니다. 여기 에서 관련 토론을 참조 하십시오 .
커스텀 컨테이너에 대한 반복자를 쉽게 정의하는 방법을 보여 주려고하지만, 컨테이너 유형에 상관없이 커스텀 반복자를 사용하여 커스텀 반복기를 쉽게 만들 수있는 c ++ 11 라이브러리를 만든 경우 불연속.
https://github.com/navyenzo/blIteratorAPI 에서 github에 있습니다.
다음은 사용자 정의 반복자를 작성하고 사용하는 간단한 단계입니다.
- "custom iterator"클래스를 작성하십시오.
- "custom container"클래스에서 typedef를 정의하십시오.
- 예를 들어 :
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 라이브러리.
보다 정확하게이 페이지는 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
이를 사용하는 것입니다.
그들은 종종 다른 방식으로 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 한정 여부와 같은 템플릿 인수를 취한다 . 필요한 경우 노드 유형도 있습니다. 그러면 모든 것이 제자리에 들어갑니다.
Just remember to parameterize (template-ize) everything that needs to be, including the copy constructor and operator==
. For the most part, the semantics of const
will create correct behavior.
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;
There are plenty of good answers but I created a template header I use that is quite concise and easy to use.
To add an iterator to your class it is only necessary to write a small class to represent the state of the iterator with 7 small functions, of which 2 are optional:
#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);
};
Then you can use it as you would expect from an STL iterator:
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
}
}
I hope it helps.
'Programing' 카테고리의 다른 글
서브 프로세스 stdout을 한 줄씩 읽으십시오 (0) | 2020.04.29 |
---|---|
파이썬에서“with”블록으로 돌아 오면 파일이 여전히 닫히나요? (0) | 2020.04.29 |
왜 정적 클래스를 상속받을 수 없습니까? (0) | 2020.04.29 |
서명 버전의 차이점-AndroidStudio에서 서명 된 APK를 생성하는 동안 V1 (Jar Signature) 및 V2 (Full APK Signature)? (0) | 2020.04.29 |
작업 생성자의 취소 토큰 : 왜? (0) | 2020.04.29 |