본문 바로가기
C C++ - STL

STL - 10 Vector Container 구현

by violetoz 2014. 2. 13.
[STL-10]Vector Container 구현해 보기|STL을 배우자
2004.06.12 10:37

[STL-9]에서 vector 멤버함수들의 용도와 사용방법에 대하여 보았습니다.
이번에는 실제 vector를 만들어 보도록 하겠습니다.
처음은 이해하기 쉽게 teamplate array를 이용하여 vector를 만든 것이며
두 번째 예제는 Accelerated C++ chapter 11에 있는 vector 구현 내용을
그대로 copy한 것입니다.
몇 몇 함수가 빠지긴 하였지만 대부분 기능이 구현된 vector의 모습니다.

단, 두 예제 모두 상속에 대한 것은 배제한 design이란 것을 알아 두시기 바랍니다.

첫 번째 예는 매우 간단한 형태입니다. array를 이용하여 자료를 관리하고 생성/복사/대입 등에 의하여 new를 호출하게 되어있고 소멸자를 통하여 모든 메모리가 해제됩니다.

vector는 random access형태가 가능한 컨테이너이므로 배열을과 포인터를 이용하면 기능적인 부분 구현에는 별다른 문제는 없습니다.

그리고 STL container는 data 생성자체가 자유공간을 사용하게 되어 있으므로 굳이 vector 객체를 new로 선언할 필요는 없습니다.

myVec.h

 1  #ifndef __MYVEC_H__
 2  #define __MYVEC_H__
 3
 4  template <class T>
 5  class myVec {
 6  private:
 7      int sz;
 8      T *v;
 9  public:
10      myVec();
11      explicit myVec(int n);      // 명시적인 선언, 0으로 초기화
12      myVec(const T &a, int n);   // 값으로 n 만큼의 크기를 만들고 초기화
13      myVec(const T *a, int n);   // 포인터로 n 만큼의 크기를 만들고 초기화
14      myVec(const myVec &rhs);    // 복사 생성자
15      myVec & operator=(const myVec &rhs); // 대입 연산자
16      myVec & operator=(const T &a);       // 전체 요소를 a로 변경
17      inline T & operator[](const int i);  // 인덱스 연산자 [i] 값 반환
18      inline const T & operator[](const int i) const;
19      inline int size() const;
20      ~myVec();
21  };
22
23  template <class T>
24  myVec<T>::myVec() : sz(0), v(0) {}
25
26  template <class T>
27  myVec<T>::myVec(int n) : sz(n), v(new T[n]) {}
28
29  template <class T>
30  myVec<T>::myVec(const T& a, int n) : sz(n), v(new T[n])
31  {
32      for(int i=0; i<n; i++)
33          v[i] = a;
34  }
35
36  template <class T>
37  myVec<T>::myVec(const T *a, int n) : sz(n), v(new T[n])
38  {
39      for(int i=0; i<n; i++)
40          v[i] = *a++;
41  }
42
43  template <class T>
44  myVec<T>::myVec(const myVec<T> &rhs) : sz(rhs.sz), v(new T[sz])
45  {
46      for(int i=0; i<sz; i++)
47          v[i] = rhs[i];
48  }
49
50  template <class T>
51  myVec<T> & myVec<T>::operator=(const myVec<T> &rhs)
52  {
53      if (this != &rhs)
54      {
55          if (sz != rhs.sz) {
56              if (v != 0) delete [] (v);
57              sz=rhs.sz;
58              v= new T[sz];
59          }
60          for (int i=0; i<sz; i++)
61              v[i]=rhs[i];
62      }
63      return *this;
64  }
65
66  template <class T>
67  myVec<T> & myVec<T>::operator=(const T &a)
68  {
69      for (int i=0; i<sz; i++)
70          v[i]=a;
71      return *this;
72  }
73
74  template <class T>
75  inline T & myVec<T>::operator[](const int i)
76  {
77      return v[i];
78  }
79
80  template <class T>
81  inline const T & myVec<T>::operator[](const int i) const
82  {
83      return v[i];
84  }
85
86  template <class T>
87  inline int myVec<T>::size() const
88  {
89      return sz;
90  }
91
92  template <class T>
93  myVec<T>::~myVec()
94  {
95      if (v != 0)
96          delete[] (v);
97  }
98
99  #endif //__MYVEC_H__

 

두 번째 예제는 Accelerated C++에서 소개한 Vec.h 내용입니다.

기본적인 메모리 관리는 <momory> 의 allocate를 이용합니다. 거의 완벽한 형태의 smart point로 사용할 수 있으므로 error에 대한 내용은 allocate가 알아서 처리하게 합니다.
잘 보실 부분이 push_back 함수인데 자료를 뒤쪽에 추가할 때 실제 입력가능한 메모리가 할당 되어 있는지를 확인 하고 만약 limit가 추가된 위치와 동이하게 되면 grow()를 이용하여 2배의 크기로 메모리를 재할당 하게 되어 있습니다.

이전에 vector에 대하여 설명할 때 메모리를 2배의 사이즈로 가지고 있다고 말한 부분이 있을겁니다. 이처럼 해서 자료가 추가될 때마다 메모리를 재설정하는 횟수를 줄이기 위함입니다. 그리고 사이즈를 늘리게 되면 메모리공간을 재할당하여 모든 자료를 재설정하는 것을 알 수 있습니다. 그리고 사이즈를 늘이는 경우 외에 자료를 중간에 넣거나 중간에서 삭제하거나 하게 되면 추가나 삭제 작업이 일어난 위치 뒤의 모든 자료는 메모리가 재설정된다는 사실도 아시는 것이 좋습니다.

clear와 소멸자 부분도 유심히 볼 필요가 있습니다. uncreate() 하게 되면 모든 자료를 alloc을 이용하여 하나씩 삭제하여 data=avail=limit=0 으로 empty 처리 해버립니다.

그리고 참고로 vector가 비었는지 확인할 때 vec.size()==0 보다는 vec.empty()를 호출하는 편이 효율적이라는 것도 알아 두세요.

 

Vec.h

  1  #ifndef VEC_H
  2  #define VEC_H
  3
  4  #include <algorithm>
  5  #include <cstddef>
  6  #include <memory>
  7
  8  #ifdef _MSC_VER
  9  #include "../minmax.h"
 10  #else
 11  using std::max;
 12  #endif
 13
 14  template <class T> class Vec {
 15  public:
 16      typedef T* iterator;
 17      typedef const T* const_iterator;
 18      typedef size_t size_type;
 19      typedef T value_type;
 20      typedef T& reference;
 21      typedef const T& const_reference;
 22
 23      Vec() { create(); }
 24      explicit Vec(size_type n, const T& t = T()) { create(n, t); }
 25
 26      Vec(const Vec& v) { create(v.begin(), v.end()); }
 27      Vec& operator=(const Vec&);
 28      ~Vec() { uncreate(); }
 29
 30      T& operator[](size_type i) { return data[i]; }
 31      const T& operator[](size_type i) const { return data[i]; }
 32
 33      void push_back(const T& t) {
 34          if (avail == limit)
 35              grow();
 36          unchecked_append(t);
 37      }
 38
 39      size_type size() const { return avail - data; }  // changed
 40
 41      iterator begin() { return data; }
 42      const_iterator begin() const { return data; }
 43
 44      iterator end() { return avail; }                 // changed
 45      const_iterator end() const { return avail; }     // changed
 46      void clear() { uncreate(); }
 47      bool empty() const { return data == avail; }
 48
 49  private:
 50      iterator data;  // first element in the `Vec'
 51      iterator avail; // (one past) the last element in the `Vec'
 52      iterator limit; // (one past) the allocated memory
 53
 54      // facilities for memory allocation
 55      std::allocator<T> alloc;    // object to handle memory allocation
 56
 57      // allocate and initialize the underlying array
 58      void create();
 59      void create(size_type, const T&);
 60      void create(const_iterator, const_iterator);
 61
 62      // destroy the elements in the array and free the memory
 63      void uncreate();
 64
 65      // support functions for `push_back'
 66      void grow();
 67      void unchecked_append(const T&);
 68  };
 69
 70  template <class T> void Vec<T>::create()
 71  {
 72      data = avail = limit = 0;
 73  }
 74
 75  template <class T> void Vec<T>::create(size_type n, const T& val)
 76  {
 77  #ifdef _MSC_VER
 78      data = alloc.allocate(n, 0);
 79  #else
 80      data = alloc.allocate(n);
 81  #endif
 82      limit = avail = data + n;
 83      std::uninitialized_fill(data, limit, val);
 84  }
 85
 86  template <class T>
 87  void Vec<T>::create(const_iterator i, const_iterator j)
 88  {
 89  #ifdef _MSC_VER
 90      data = alloc.allocate(j - i, 0);
 91  #else
 92      data = alloc.allocate(j - i);
 93  #endif
 94      limit = avail = std::uninitialized_copy(i, j, data);
 95  }
 96
 97  template <class T> void Vec<T>::uncreate()
 98  {
 99      if (data) {
100          // destroy (in reverse order) the elements that were constructed
101          iterator it = avail;
102          while (it != data)
103              alloc.destroy(--it);
104
105          // return all the space that was allocated
106          alloc.deallocate(data, limit - data);
107      }
108      // reset pointers to indicate that the `Vec' is empty again
109      data = limit = avail = 0;
110
111  }
112
113  template <class T> void Vec<T>::grow()
114  {
115      // when growing, allocate twice as much space as currently in use
116      size_type new_size = max(2 * (limit - data), ptrdiff_t(1));
117
118      // allocate new space and copy existing elements to the new space
119  #ifdef _MSC_VER
120      iterator new_data = alloc.allocate(new_size, 0);
121  #else
122      iterator new_data = alloc.allocate(new_size);
123  #endif
124      iterator new_avail = std::uninitialized_copy(data, avail, new_data);
125
126      // return the old space
127      uncreate();
128
129      // reset pointers to point to the newly allocated space
130      data = new_data;
131      avail = new_avail;
132      limit = data + new_size;
133  }
134
135  // assumes `avail' points at allocated, but uninitialized space
136  template <class T> void Vec<T>::unchecked_append(const T& val)
137  {
138      alloc.construct(avail++, val);
139  }
140
141  template <class T>
142  Vec<T>& Vec<T>::operator=(const Vec& rhs)
143  {
144      // check for self-assignment
145      if (&rhs != this) {
146
147          // free the array in the left-hand side
148          uncreate();
149
150          // copy elements from the right-hand to the left-hand side
151          create(rhs.begin(), rhs.end());
152      }
153      return *this;
154  }
155
156  #endif


'C C++ - STL' 카테고리의 다른 글

STL - 12 auto_ptr  (0) 2014.02.13
STL - 11 Function Object (함수 - 객체)  (0) 2014.02.13
STL - 9 find, find_if  (0) 2014.02.13
STL - 8 Vector 라이브러리  (0) 2014.02.13
STL - 7 Sequence Container  (0) 2014.02.13