나만의 STL 컨테이너 작성


120

어떤 STL컨테이너 처럼 작동하는 새 컨테이너를 작성하는 방법에 대한 지침이 있습니까?


7
기존 표준 컨테이너의 구현을보고 함수, 반환 유형, 연산자 오버로드, 중첩 유형, 메모리 관리 등을 이해하려고 노력하십시오.
Nawaz 2011 년

일반적으로 msdn 또는 표준에서 수행중인 작업과 개념 상 가장 가까운 컨테이너의 멤버 함수 프로토 타입을 복사하는 것으로 시작합니다. ( cplusplus.com C ++ 11의 기능을 가지고 있지 않으며, www.sgi.com 일치하지 않음)
오리 음매

@Mooing Duck : msdn이 sgi보다 표준에 더 가깝다고 생각하십니까?
Dani

3
확실히 그렇습니다. MSDN은 최신 버전입니다. SGI는 Pre-Standard입니다
Puppy

9
최고의 온라인 참조 (완전성, 정확성 및 특히 유용성)는 지금까지 cppreference.com입니다. 또한 라이브러리 외에 많은 언어 기능을 설명합니다. 그리고 위키이므로 cplusplus.com보다 오류가 적어야합니다.
rubenvb 2013-08-20

답변:


209

여기에 그 § 23.2.1 \ 4 주에서 함께 조합해서 일련의 의사 컨테이너 I의 iterator_category중 하나 여야는 std::input_iterator_tag, std::output_iterator_tag, std::forward_iterator_tag, std::bidirectional_iterator_tag, std::random_access_iterator_tag. 또한 아래 내용은 기술적으로 필요한 것보다 더 엄격하지만 이것이 아이디어입니다. 대부분의 "표준"함수는 반복 자라는 멋진 기능으로 인해 기술적으로 선택 사항입니다.

template <class T, class A = std::allocator<T> >
class X {
public:
    typedef A allocator_type;
    typedef typename A::value_type value_type; 
    typedef typename A::reference reference;
    typedef typename A::const_reference const_reference;
    typedef typename A::difference_type difference_type;
    typedef typename A::size_type size_type;

    class iterator { 
    public:
        typedef typename A::difference_type difference_type;
        typedef typename A::value_type value_type;
        typedef typename A::reference reference;
        typedef typename A::pointer pointer;
        typedef std::random_access_iterator_tag iterator_category; //or another tag

        iterator();
        iterator(const iterator&);
        ~iterator();

        iterator& operator=(const iterator&);
        bool operator==(const iterator&) const;
        bool operator!=(const iterator&) const;
        bool operator<(const iterator&) const; //optional
        bool operator>(const iterator&) const; //optional
        bool operator<=(const iterator&) const; //optional
        bool operator>=(const iterator&) const; //optional

        iterator& operator++();
        iterator operator++(int); //optional
        iterator& operator--(); //optional
        iterator operator--(int); //optional
        iterator& operator+=(size_type); //optional
        iterator operator+(size_type) const; //optional
        friend iterator operator+(size_type, const iterator&); //optional
        iterator& operator-=(size_type); //optional            
        iterator operator-(size_type) const; //optional
        difference_type operator-(iterator) const; //optional

        reference operator*() const;
        pointer operator->() const;
        reference operator[](size_type) const; //optional
    };
    class const_iterator {
    public:
        typedef typename A::difference_type difference_type;
        typedef typename A::value_type value_type;
        typedef typename const A::reference reference;
        typedef typename const A::pointer pointer;
        typedef std::random_access_iterator_tag iterator_category; //or another tag

        const_iterator ();
        const_iterator (const const_iterator&);
        const_iterator (const iterator&);
        ~const_iterator();

        const_iterator& operator=(const const_iterator&);
        bool operator==(const const_iterator&) const;
        bool operator!=(const const_iterator&) const;
        bool operator<(const const_iterator&) const; //optional
        bool operator>(const const_iterator&) const; //optional
        bool operator<=(const const_iterator&) const; //optional
        bool operator>=(const const_iterator&) const; //optional

        const_iterator& operator++();
        const_iterator operator++(int); //optional
        const_iterator& operator--(); //optional
        const_iterator operator--(int); //optional
        const_iterator& operator+=(size_type); //optional
        const_iterator operator+(size_type) const; //optional
        friend const_iterator operator+(size_type, const const_iterator&); //optional
        const_iterator& operator-=(size_type); //optional            
        const_iterator operator-(size_type) const; //optional
        difference_type operator-(const_iterator) const; //optional

        reference operator*() const;
        pointer operator->() const;
        reference operator[](size_type) const; //optional
    };

    typedef std::reverse_iterator<iterator> reverse_iterator; //optional
    typedef std::reverse_iterator<const_iterator> const_reverse_iterator; //optional

    X();
    X(const X&);
    ~X();

    X& operator=(const X&);
    bool operator==(const X&) const;
    bool operator!=(const X&) const;
    bool operator<(const X&) const; //optional
    bool operator>(const X&) const; //optional
    bool operator<=(const X&) const; //optional
    bool operator>=(const X&) const; //optional

    iterator begin();
    const_iterator begin() const;
    const_iterator cbegin() const;
    iterator end();
    const_iterator end() const;
    const_iterator cend() const;
    reverse_iterator rbegin(); //optional
    const_reverse_iterator rbegin() const; //optional
    const_reverse_iterator crbegin() const; //optional
    reverse_iterator rend(); //optional
    const_reverse_iterator rend() const; //optional
    const_reverse_iterator crend() const; //optional

    reference front(); //optional
    const_reference front() const; //optional
    reference back(); //optional
    const_reference back() const; //optional
    template<class ...Args>
    void emplace_front(Args&&...); //optional
    template<class ...Args>
    void emplace_back(Args&&...); //optional
    void push_front(const T&); //optional
    void push_front(T&&); //optional
    void push_back(const T&); //optional
    void push_back(T&&); //optional
    void pop_front(); //optional
    void pop_back(); //optional
    reference operator[](size_type); //optional
    const_reference operator[](size_type) const; //optional
    reference at(size_type); //optional
    const_reference at(size_type) const; //optional

    template<class ...Args>
    iterator emplace(const_iterator, Args&&...); //optional
    iterator insert(const_iterator, const T&); //optional
    iterator insert(const_iterator, T&&); //optional
    iterator insert(const_iterator, size_type, T&); //optional
    template<class iter>
    iterator insert(const_iterator, iter, iter); //optional
    iterator insert(const_iterator, std::initializer_list<T>); //optional
    iterator erase(const_iterator); //optional
    iterator erase(const_iterator, const_iterator); //optional
    void clear(); //optional
    template<class iter>
    void assign(iter, iter); //optional
    void assign(std::initializer_list<T>); //optional
    void assign(size_type, const T&); //optional

    void swap(X&);
    size_type size() const;
    size_type max_size() const;
    bool empty() const;

    A get_allocator() const; //optional
};
template <class T, class A = std::allocator<T> >
void swap(X<T,A>&, X<T,A>&); //optional

또한 컨테이너를 만들 때마다 다음과 같이 클래스로 테스트합니다.

#include <cassert>
struct verify;
class tester {
    friend verify;
    static int livecount;
    const tester* self;
public:
    tester() :self(this) {++livecount;}
    tester(const tester&) :self(this) {++livecount;}
    ~tester() {assert(self==this);--livecount;}
    tester& operator=(const tester& b) {
        assert(self==this && b.self == &b);
        return *this;
    }
    void cfunction() const {assert(self==this);}
    void mfunction() {assert(self==this);}
};
int tester::livecount=0;
struct verify {
    ~verify() {assert(tester::livecount==0);}
}verifier;

tester개체의 컨테이너를 만들고 컨테이너 function()를 테스트 할 때 각 개체를 호출 합니다. 전역 tester개체를 만들지 마십시오 . 컨테이너가 어디에서나 속이는 경우,이 tester클래스는 assert실수로 어딘가에서 속임수를 썼다는 것을 알게 될 것입니다.


1
이건 재미 있네. 테스터는 어떻게 작동합니까? 사소한 ( ';'누락) 몇 가지 구문 분석 오류가 있지만 해당 소멸자가 어떻게 작동하는지 확실하지 않습니다. 오, 당신은 assert(tester::livecount == 0);. 이 테스터 프레임 워크가 어떻게 작동하는지 아직 확실하지 않습니다. 예를 들어 주시겠습니까?
Adrian

2
테스터에는 자신에 대한 포인터 인 단일 비 정적 멤버가 있으며 소멸자와 멤버는 유효하지 않은 memcpy일이 발생 하지 않았 는지 확인하는 방법 입니다. (테스트는 완벽하지는 않지만 일부를 포착합니다). 는 livecount반드시 컨테이너가 생성자와 소멸자의 같은 수라고 할 간단한 누설 감지기이다.
Mooing Duck 2014

좋아, 알지만 어떻게 반복자를 테스트합니까? BTW, 나는 당신이 의미 verifier하지 않았다고 생각합니다 varifier.
Adrian

4
@Adrian 아니요, 컨테이너를 작성한 다음 컨테이너에 여러 개를 넣고 컨테이너로 작업을 수행하여 실수로 memcpy하지 않았는지 확인하고 모든 소멸자를 호출하도록 재 멤버했습니다.
Mooing Duck 2014

1
나는에서 반복자를 상속 제안 할 수 있습니다 std::iterator헤더에서<iterator>
sp2danny

28

컨테이너 구현을 위해 C ++ 표준이 부과하는 컨테이너 및 요구 사항에 대한 C ++ 표준 섹션을 읽어야합니다.

C ++ 03 표준의 관련 장은 다음과 같습니다.

섹션 23.1 컨테이너 요구 사항

C ++ 11 표준의 관련 장은 다음과 같습니다.

섹션 23.2 컨테이너 요구 사항

C ++ 11 표준의 최종 초안은 여기에서 무료로 사용할 수 있습니다 .

컨테이너 사용자의 관점에서 요구 사항을 이해하는 데 도움이되는 훌륭한 책을 읽을 수도 있습니다. 내 마음에 쉽게 떠오른 두 가지 훌륭한 책은 다음과 같습니다.

효과적인 STL 에 의해스콧 마이어스
튜토리얼 및 참조 : C ++ 표준 라이브러리 에 의해니콜라이 Josutils


6

다음은 기본적으로 래퍼 std::vector이며 STL 반복기를 모방하는 자체 (그러나 실제) 반복기 를 갖는 가짜 벡터의 매우 단순한 구현입니다 . 다시 말하지만, 이터레이터는 const_iterator, 유효성 검사 등과 같은 많은 개념을 건너 뛰는 매우 단순 합니다.

코드는 즉시 실행할 수 있습니다.

#include <iostream>
#include <string>
#include <vector>

template<typename T>
struct It
{
    std::vector<T>& vec_;
    int pointer_;

    It(std::vector<T>& vec) : vec_{vec}, pointer_{0} {}

    It(std::vector<T>& vec, int size) : vec_{vec}, pointer_{size} {}

    bool operator!=(const It<T>& other) const
    {
        return !(*this == other);
    }

    bool operator==(const It<T>& other) const
    {
        return pointer_ == other.pointer_;
    }

    It& operator++()
    {
        ++pointer_;            
        return *this;
    }

    T& operator*() const
    {
        return vec_.at(pointer_);   
    }
};

template<typename T>
struct Vector
{
    std::vector<T> vec_;

    void push_back(T item)
    {
        vec_.push_back(item);
    };

    It<T> begin()
    {
        return It<T>(vec_);
    }

    It<T> end()
    {
        return It<T>(vec_, vec_.size());
    }
};

int main()
{
  Vector<int> vec;
  vec.push_back(1);
  vec.push_back(2);
  vec.push_back(3);

  bool first = true;
  for (It<int> it = vec.begin(); it != vec.end(); ++it)
  {
      if (first) //modify container once while iterating
      {
          vec.push_back(4);
          first = false;
      }

      std::cout << *it << '\n'; //print it 
      (*it)++;                  //change it
  }

  for (It<int> it = vec.begin(); it != vec.end(); ++it)
  {
      std::cout << *it << '\n'; //should see changed value
  }
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.