STL 소스 노트 (12) - 시퀀스 용기 의 deque (2)

24234 단어 STLdeque
STL 소스 노트 (12) - 시퀀스 용기 의 deque (2)
deque 데이터 구조 재 론
우 리 는 deque 가 map 를 통 해 서로 독립 된 연속 공간 을 관리 하 는 것 을 알 고 있 습 니 다. 왜냐하면 dequeiterator 의 특수 한 디자인 으로 사용 할 때 연속 적 인 것 같 습 니 다.deque 가 생 겼 습 니 다.iterator 의 기초 (예 를 들 어 다시 불 러 오 는 조작 부호 등) 는 우리 가 용 기 를 실현 하 는 몇 가지 방법 에 매우 편리 하 다.vector 와 마찬가지 로 deque 도 하나의 start 를 유지 하고 finish 두 개의 교체 기 를 유지 합 니 다. start 는 용기 의 한 요 소 를 가리 키 고 finish 는 마지막 요소 의 다음 위 치 를 가리 키 며 미시적 으로 start 는 map 관리의 첫 번 째 버퍼 의 첫 번 째 요 소 를 가리 키 며 finish 는 마지막 버퍼 의 한 요 소 를 관리 합 니 다.(이 요 소 는 전체 용기 의 마지막 요소 입 니 다) 다음 위치 입 니 다. STL 소스 코드 에 서 는 이 포인터 들 을 기본 클래스 로 정의 하고 하위 클래스 에서 직접 사용 합 니 다.
template <class _Tp, class _Alloc>
class _Deque_base {//  
public:
  typedef _Deque_iterator<_Tp,_Tp&,_Tp*>             iterator;
  typedef _Deque_iterator<_Tp,const _Tp&,const _Tp*> const_iterator;
  //...
  //...
protected:
  iterator _M_start;//start  
  iterator _M_finish;//finish  
};

//-----------------------   -----------------
template <class _Tp, class _Alloc = __STL_DEFAULT_ALLOCATOR(_Tp) >
class deque : protected _Deque_base<_Tp, _Alloc> {//  
  typedef _Deque_base<_Tp, _Alloc> _Base;
public:                         // Iterators
  typedef typename _Base::iterator       iterator;
  typedef typename _Base::const_iterator const_iterator;

protected:
  using _Base::_M_map;//map    
  using _Base::_M_map_size;
  using _Base::_M_start;//start  
  using _Base::_M_finish;//finish  

상기 기초 가 있 으 면 deque 에서 정 의 된 많은 함수 들 이 쉽게 완성 할 수 있 습 니 다. (많은 조작 은 deque iterator 에서 합 니 다)
  iterator begin() { return _M_start; }//             
  iterator end() { return _M_finish; }//                   
  const_iterator begin() const { return _M_start; }//const   
  const_iterator end() const { return _M_finish; }//const   
  //---------------------   -----------------------------------------------
  reference front() { return *_M_start; }//          
  reference back() {//          ,(                )
    iterator __tmp = _M_finish;
    --__tmp;
    return *__tmp;
  }
  //  :         *(_M_finish-1)   ,    deque_iterator  ,             ,    :  

  _Self operator-(difference_type __n) const {
    _Self __tmp = *this;
    return __tmp -= __n;//       -=
  }

  const_reference front() const { return *_M_start; }//const   
  const_reference back() const {//const   
    const_iterator __tmp = _M_finish;
    --__tmp;
    return *__tmp;
  }
  //===================   =====================
  size_type size() const { return _M_finish - _M_start; }
  size_type max_size() const { return size_type(-1); }
  bool empty() const { return _M_finish == _M_start; }

deque 의 구조 와 메모리 관리
deque 데이터 구조
책 에서 4deque-test.cpp 예 를 들 어 버퍼 의 크기 는 32bytes 라 고 언급 했 는데 여 기 는 후 선생님 의 코드 에서 실제 적 으로 보 여 주 었 습 니 다.
deque<int,alloc,8>ideq(20,9);

템 플 릿 실 삼 8 은 버퍼 크기 가 8 개의 요 소 를 나 타 냅 니 다. 여기 가 int 형 (4 바이트) 이기 때문에 크기 가 8*sizeof(int)=32bytes 입 니 다. 위의 다른 템 플 릿 실 삼 은 공간 설정 기 입 니 다.원본 코드 에서 공간 설정 기 를 정의 하 는 방식 은 vector 와 유사 합 니 다. 모두 simple alloc 를 통 해 퍼 가기 도 하고 deque 의 기본 클래스 에서 정의 하 며 하위 클래스 에서 도 사 용 됩 니 다.
template <class _Tp, class _Alloc>
class _Deque_base {//  
//...
typedef _Alloc allocator_type;//       
typedef simple_alloc<_Tp, _Alloc>  _Node_alloc_type;
typedef simple_alloc<_Tp*, _Alloc> _Map_alloc_type;
_Tp* _M_allocate_node()//      
    { return _Node_alloc_type::allocate(__deque_buf_size(sizeof(_Tp))); }
void _M_deallocate_node(_Tp* __p)//  
    { _Node_alloc_type::deallocate(__p, __deque_buf_size(sizeof(_Tp))); }
_Tp** _M_allocate_map(size_t __n) //    map   
    { return _Map_alloc_type::allocate(__n); }
void _M_deallocate_map(_Tp** __p, size_t __n) //  
    { _Map_alloc_type::deallocate(__p, __n); }

protected:
  void _M_initialize_map(size_t);
  void _M_create_nodes(_Tp** __nstart, _Tp** __nfinish);
  void _M_destroy_nodes(_Tp** __nstart, _Tp** __nfinish);
//...
};

//=================>   <========================
template <class _Tp, class _Alloc = __STL_DEFAULT_ALLOCATOR(_Tp) >
class deque : protected _Deque_base<_Tp, _Alloc> {
  //...
  typedef _Deque_base<_Tp, _Alloc> _Base;
  typedef typename _Base::allocator_type allocator_type;
  allocator_type get_allocator() const { return _Base::get_allocator(); }
  using _Base::_M_initialize_map;
  using _Base::_M_create_nodes;
  using _Base::_M_destroy_nodes;

  using _Base::_M_allocate_node;
  using _Base::_M_deallocate_node;
  using _Base::_M_allocate_map;
  using _Base::_M_deallocate_map;
  //...
};

deque 구조 기 상세 설명
모든 것 이 준비 되 어 있 습 니 다. 이 제 는 상기 정 의 된 지침, 공간 설정 기 등 을 사용 하여 deque 의 구 조 를 만 들 고 배정 해 야 합 니 다. deque 의 constructor 구조 기: 앞서 언급 한 많은 것 과 마찬가지 로 기본 클래스 에서 도 이 루어 집 니 다. 구조 함 수 는 지침 을 초기 화 하 는 것 외 에 deque 를 위해 공간 을 배정 해 야 합 니 다. 여 기 는 공간 설정 기 를 사용 해 야 합 니 다.(위의 코드 에는 이미 봉인 이 되 어 있 습 니 다) 책 에서 제공 하 는 create_map_and_nodes 방법 은 SGI STL 소스 코드 에 이러한 함수 가 있 습 니 다.
//              ,           
template <class _Tp, class _Alloc>//             
void
_Deque_base<_Tp,_Alloc>::_M_initialize_map(size_t __num_elements)
{
  //      =(    /        ) +1
  //              
  size_t __num_nodes = 
    __num_elements / __deque_buf_size(sizeof(_Tp)) + 1;

    //  map        (           “ ”)
    //   8 ,   “       2”(  )
    //enum { _S_initial_map_size = 8 };      
  _M_map_size = max((size_t) _S_initial_map_size, __num_nodes + 2);
  _M_map = _M_allocate_map(_M_map_size);//    ,  _M_map_size   

  //  map  2        ,        map         
  //    ,          ,         
  _Tp** __nstart = _M_map + (_M_map_size - __num_nodes) / 2;
  _Tp** __nfinish = __nstart + __num_nodes;//            1(   ,               )

    /* STL_TRY STL_UNWIND     try catch    */


  __STL_TRY {
      //    (      )       。
    _M_create_nodes(__nstart, __nfinish);//  [__nstart,__nfinish)        
  }
  __STL_UNWIND((_M_deallocate_map(_M_map, _M_map_size), 
                _M_map = 0, _M_map_size = 0));//try catch block           
    //       (deque_iterator)                   
  _M_start._M_set_node(__nstart);//  _M_start         (          ,      ,      )
  _M_finish._M_set_node(__nfinish - 1);//  _M_finish         
  _M_start._M_cur = _M_start._M_first;//           
  _M_finish._M_cur = _M_finish._M_first +
               __num_elements % __deque_buf_size(sizeof(_Tp));//              
}

//====================>   <=====================
template <class _Tp, class _Alloc>
void _Deque_base<_Tp,_Alloc>::_M_create_nodes(_Tp** __nstart, _Tp** __nfinish)
{
  _Tp** __cur;
  __STL_TRY {
    for (__cur = __nstart; __cur < __nfinish; ++__cur)//   __cur<=__nfinish
      *__cur = _M_allocate_node();//     ,    512/sizeof(T);T   
      //     map              
  }
  __STL_UNWIND(_M_destroy_nodes(__nstart, __cur));//           (    try catch block)
}

deque 공간 관리
사실은 Deque 양 방향 개 구 부 특성 으로 인해 push back () 과 push front () 두 가지 조작 을 제공 합 니 다. 또한 이 deque 는 실제 적 으로 여러 개의 버퍼 와 중간 제어 map 로 연속 공간 을 모 의 하기 때문에 구조 요 소 를 추가 할 때 현재 버퍼 가 충분 한 지 고려 해 야 합 니 다. 부족 하면 중간 제어 map 의 노드 포인 터 를 뒤로 (push back) 하거나 앞으로 가 야 합 니 다.(push front) 작은 칸 을 옮 깁 니 다. 물론 중간 제어 맵 도 확장 할 노드 가 충분 하지 않 으 면 맵 을 다시 정리 해 야 합 니 다.
void push_back(const value_type& __t) {
    if (_M_finish._M_cur != _M_finish._M_last - 1) {
      construct(_M_finish._M_cur, __t);//         ,    
      ++_M_finish._M_cur;
    }
    else
      _M_push_back_aux(__t);//         ,  map     。
  }

// Called only if _M_finish._M_cur == _M_finish._M_last - 1.
//     
template <class _Tp, class _Alloc>
void deque<_Tp,_Alloc>::_M_push_back_aux(const value_type& __t)
{
  value_type __t_copy = __t;
  _M_reserve_map_at_back();//            map
  *(_M_finish._M_node + 1) = _M_allocate_node();//           
  __STL_TRY {
    construct(_M_finish._M_cur, __t_copy);//  ,              
    _M_finish._M_set_node(_M_finish._M_node + 1);//       ,    
    _M_finish._M_cur = _M_finish._M_first;//    
  }
  __STL_UNWIND(_M_deallocate_node(*(_M_finish._M_node + 1)));//        
}

//----------------->   <---------------------------------
void push_front(const value_type& __t) {
    if (_M_start._M_cur != _M_start._M_first) {//      
      construct(_M_start._M_cur - 1, __t);//      
      --_M_start._M_cur;//    
    }
    else
      _M_push_front_aux(__t);//          
  }
template <class _Tp, class _Alloc>
void  deque<_Tp,_Alloc>::_M_push_front_aux(const value_type& __t)
{
  value_type __t_copy = __t;
  _M_reserve_map_at_front();
  *(_M_start._M_node - 1) = _M_allocate_node();//     map        
  __STL_TRY {
    _M_start._M_set_node(_M_start._M_node - 1);//      
    _M_start._M_cur = _M_start._M_last - 1;//      
    construct(_M_start._M_cur, __t_copy);//     
  }
  __STL_UNWIND((++_M_start, _M_deallocate_node(*(_M_start._M_node - 1))));
} 

상기 두 가지 관건 적 인 조작 (실제 적 으로 하나의 효과) 이 있 는데 그것 이 바로 map 노드 를 확대 하 는 것 이다. _M_reserve_map_at_front()&_M_reserve_map_at_back() 안의 실제 조작 은 _M_reallocate_map() 에 의 해 이 루어 진다.
void _M_reserve_map_at_back (size_type __nodes_to_add = 1) {
    if (__nodes_to_add + 1 > _M_map_size - (_M_finish._M_node - _M_map))
    //map          
      _M_reallocate_map(__nodes_to_add, false);//     ,  ,     
  }

물론 대응 하 는 것 pop_back()pop_front() 은 전단 과 백 엔 드 에 삽입 하 는 것 과 유사 하 므 로 전체 버퍼 를 방출 해 야 하 는 지 여 부 를 판단 해 야 합 니 다.
맨 끝 에 삭제 요 소 를 삽입 하 는 것 외 에 deque 는 clear (), erase (), insert () 등 흔히 볼 수 있 는 방법 도 있다.
//clear()
template <class _Tp, class _Alloc> 
void deque<_Tp,_Alloc>::clear()
{
  for (_Map_pointer __node = _M_start._M_node + 1;
       __node < _M_finish._M_node;
       ++__node) {
    destroy(*__node, *__node + _S_buffer_size());
    _M_deallocate_node(*__node);
  }//              

  if (_M_start._M_node != _M_finish._M_node) {//            
    destroy(_M_start._M_cur, _M_start._M_last);//            
    destroy(_M_finish._M_first, _M_finish._M_cur);//            
    _M_deallocate_node(_M_finish._M_first);//       
  }
  else//       ,           
    destroy(_M_start._M_cur, _M_finish._M_cur);//       ,      

  _M_finish = _M_start;//    
}
//erase()         
iterator erase(iterator __pos) {
    iterator __next = __pos;
    ++__next;
    difference_type __index = __pos - _M_start;//          
    if (size_type(__index) < (this->size() >> 1)) {             
      copy_backward(_M_start, __pos, __next);//      (            )
      pop_front();//         
    }
    else {
      copy(__next, _M_finish, __pos);//      
      pop_back();//          
    }
    return _M_start + __index;//      
  }

//============>>   <<========================

//earse()     
template <class _Tp, class _Alloc>
typename deque<_Tp,_Alloc>::iterator 
deque<_Tp,_Alloc>::erase(iterator __first, iterator __last)
{
  if (__first == _M_start && __last == _M_finish) {
    clear();//       
    return _M_finish;
  }
  else {
    difference_type __n = __last - __first;//       
    difference_type __elems_before = __first - _M_start;//           
    if (__elems_before < difference_type((this->size() - __n) / 2)) {//       
      copy_backward(_M_start, __first, __last);//              last  
      iterator __new_start = _M_start + __n;//      
      destroy(_M_start, __new_start);//      
      _M_destroy_nodes(__new_start._M_node, _M_start._M_node);//       
      _M_start = __new_start;//      
    }
    else {//       
      copy(__last, _M_finish, __first);
      iterator __new_finish = _M_finish - __n;
      destroy(__new_finish, _M_finish);
      _M_destroy_nodes(__new_finish._M_node + 1, _M_finish._M_node + 1);
      _M_finish = __new_finish;
    }
    return _M_start + __elems_before;
  }
}
iterator insert(iterator position, const value_type& __x) {
    if (position._M_cur == _M_start._M_cur) {//         
      push_front(__x);//  push_front()
      return _M_start;
    }
    else if (position._M_cur == _M_finish._M_cur) {//      
      push_back(__x);//  push_back()
      iterator __tmp = _M_finish;
      --__tmp;
      return __tmp;
    }
    else {
      return _M_insert_aux(position, __x);
    }
  }
//===========>>   <<=====================
template <class _Tp, class _Alloc>
typename deque<_Tp, _Alloc>::iterator
deque<_Tp,_Alloc>::_M_insert_aux(iterator __pos, const value_type& __x)
{
  difference_type __index = __pos - _M_start;//          
  value_type __x_copy = __x;
  if (size_type(__index) < this->size() / 2) {//   
    push_front(front());//           
    iterator __front1 = _M_start;//  
    ++__front1;
    iterator __front2 = __front1;
    ++__front2;
    __pos = _M_start + __index;
    iterator __pos1 = __pos;
    ++__pos1;
    copy(__front2, __pos1, __front1);//    ,            
  }
  else {//   
    push_back(back());
    iterator __back1 = _M_finish;
    --__back1;
    iterator __back2 = __back1;
    --__back2;
    __pos = _M_start + __index;
    copy_backward(__pos, __back2, __back1);
  }
  *__pos = __x_copy;//         
  return __pos;
}

좋은 웹페이지 즐겨찾기