1 Related header files

stl_vector.h
vector.h
vector
Copy the code

2 Memory Allocation

Vector allocates memory by default using __default_alloc_template. This alloc_template alloc_template alloc_template alloc_template is thread-safe

3 Vector buffer

Vector is a derivative of _Vector_base, which has three member variables

protected:
  _Tp* _M_start;
  _Tp* _M_finish;
  _Tp* _M_end_of_storage;
Copy the code

As we all know, a vector is a more advanced array, and an array necessarily contains a contiguous buffer. As shown below, _M_start represents the left real boundary of the data area in the buffer, _M_finish represents the right virtual boundary of the data area in the buffer, and _M_end_of_storage points to the right virtual boundary of the memory buffer

4 vector iterator

Vector uses contiguous physical memory space, so iterators are represented directly by raw Pointers

  typedef _Tp value_type;
  
  typedef value_type* iterator;   // Vector iterators are normal Pointers
  typedef const value_type* const_iterator;
  
  typedef reverse_iterator<const_iterator> const_reverse_iterator;
  typedef reverse_iterator<iterator> reverse_iterator;
Copy the code

In stl_iterator.h, a reverse_iterator is a wrapper around a forward iterator.

5 Vector API implementation

5.1 Default constructors

For example, the vector < int > a; In this case, the vector size is zero. To save memory, vector

does not actively allocate memory or create buffers.

    explicit vector(const allocator_type& __a = allocator_type())
    : _Base(__a) {}
  
    _Vector_base(const _Alloc&)
    : _M_start(0), _M_finish(0), _M_end_of_storage(0) {}
Copy the code

5.2 Construct a vector from size and initials

For example, vector

a(100, 0), in which case _Vector_base first allocates n*sizeof(Tp) memory using a memory allocator of type _Alloc, and then populates the initial values in the vector constructor

  vector(size_type __n, const _Tp& __value,
         const allocator_type& __a = allocator_type()) 
    : _Base(__n, __a)
    { _M_finish = uninitialized_fill_n(_M_start, __n, __value); }
    
  explicit vector(size_type __n)
    : _Base(__n, allocator_type())
    { _M_finish = uninitialized_fill_n(_M_start, __n, _Tp()); }
    

  _Vector_base(size_t __n, const _Alloc&)
    : _M_start(0), _M_finish(0), _M_end_of_storage(0) 
  {
    _M_start = _M_allocate(__n);
    _M_finish = _M_start;
    _M_end_of_storage = _M_start + __n;
  }
Copy the code

5.3 Copy Constructor

For example,

vector<int> a(100.0); 
vector<int> b(a);     // Copy constructor
Copy the code

The _Vector_base first allocates memory for n*sizeof(Tp) using an allocator of type _Alloc. The Vector constructor then copies the value of __x into the local buffer.

  vector(const vector<_Tp, _Alloc>& __x) 
    : _Base(__x.size(), __x.get_allocator())
    { _M_finish = uninitialized_copy(__x.begin(), __x.end(), _M_start); }
Copy the code

5.4 Constructing a vector from iterators

For example,

vector<int> a(100.0);
vector<int> b(a.begin(), a.begin() + 10);
Copy the code

Note that _Vector_base does not allocate last-first memory in advance. Instead, _M_allocate is allocated in vector::_M_range_initialize.

Note that you need to distinguish whether _InputIterator is an integer

  // Check whether it's an integral type. If so, it's not an iterator.
  template <class _InputIterator>
  vector(_InputIterator __first, _InputIterator __last,
         const allocator_type& __a = allocator_type()) : _Base(__a) {
    typedef typename _Is_integer<_InputIterator>::_Integral _Integral;
    _M_initialize_aux(__first, __last, _Integral());
  }

  template <class _Integer>
  void _M_initialize_aux(_Integer __n, _Integer __value, __true_type) {
    _M_start = _M_allocate(__n);
    _M_end_of_storage = _M_start + __n; 
    _M_finish = uninitialized_fill_n(_M_start, __n, __value);
  }
Copy the code

5.5 Destructors

For example vector

a(100, 0); When a is reclaimed, we call vector::~vector to reclaim all the contained elements, and then _Vector_base::~_Vector_base to free up the allocated memory.

  ~vector() { destroy(_M_start, _M_finish); }
  ~_Vector_base() { _M_deallocate(_M_start, _M_end_of_storage - _M_start); }
Copy the code

5.6 the push_back

There are two cases,

  • Execute on a buffer if there is one availableinplacement new
  • If no buffer is available, execute againreallocGenerate a new buffer and copy the data from the old buffer to the new buffer

Code details: First determine if there is free memory, and if so, perform inplacement New on it

  void push_back(const _Tp& __x) {
    if(_M_finish ! = _M_end_of_storage) {construct(_M_finish, __x);
      ++_M_finish;
    }
    else
      _M_insert_aux(end(), __x);
  }
  

template <class _T1.class _T2>
inline void _Construct(_T1* __p, const _T2& __value) {
  new ((void*) __p) _T1(__value);
}  
Copy the code

If push_back (capacity) is 0, then expand (capacity) to 1; otherwise, the new capacity is double the old capacity.

template <class _Tp.class _Alloc>
void 
vector<_Tp, _Alloc>::_M_insert_aux(iterator __position)
{
  if(_M_finish ! = _M_end_of_storage) {construct(_M_finish, *(_M_finish - 1));
    ++_M_finish;
    copy_backward(__position, _M_finish - 2, _M_finish - 1);
    *__position = _Tp();
  }
  else {
    const size_type __old_size = size(a);constsize_type __len = __old_size ! =0 ? 2 * __old_size : 1;
    iterator __new_start = _M_allocate(__len);
    iterator __new_finish = __new_start;
    __STL_TRY {
      __new_finish = uninitialized_copy(_M_start, __position, __new_start);
      construct(__new_finish);
      ++__new_finish;
      __new_finish = uninitialized_copy(__position, _M_finish, __new_finish);
    }
    __STL_UNWIND((destroy(__new_start,__new_finish), 
                  _M_deallocate(__new_start,__len)));
    destroy(begin(), end()); _M_deallocate(_M_start, _M_end_of_storage - _M_start); _M_start = __new_start; _M_finish = __new_finish; _M_end_of_storage = __new_start + __len; }}Copy the code

The invocation link of memory allocation is

_M_allocate -> simple_alloc<_Tp, _Alloc>::allocate -> _Alloc::allocate (_Alloc defaults to __STL_DEFAULT_ALLOCATOR(_Tp)) -> allocator<_Tp> -> __default_alloc_template<__NODE_ALLOCATOR_THREADS, 0>Copy the code

The implementation of __default_alloc_template is detailed in STL source code analysis – Memory allocation

5.7 other

  • Begin, returns a pointer to the left edge of the data area
  • End, returns a pointer to the right virtual boundary of the data area
  • Rbegin, returns the reverse iterator corresponding to the right virtual boundary of the data area
  • Rend, returns the reverse iterator corresponding to the left edge of the data range
  • Size, return end()-begin(), the number of elements in the data area
  • Max_size, which returns the maximum number of size_type that size_t can represent
  • Capacity, return _M_end_of_storage-begin (); The number of elements a buffer can hold
  • Exchange swap,_M_start._M_finish._M_end_of_storageThese three Pointers will do
  • The insert,push_back, the difference is that insert requires moving all elements after the insertion position
  • Pop_back destroys the trailing element
  • Erase destroys the specified element or range and copies all elements after that element to the head
  • Resize, there are two cases
    • The new size is larger than the current size, insertnew_size - old_sizeA zero value
    • The new size is smaller than now, for the tailold_size - new_sizeElement executioneraseNote that resize does not free free memory on the buffer
  • Operator =, assuming b is assigned to A in one of three cases
    • If B. size > A. Capacity, A runs _M_allocate_and_copy to apply for and initialize a new buffer, and then runs _M_deallocate to release the old buffer
    • If a.size > b.size, copy B directly to A and destroy unwanted objects in A
    • If a.size < b.size < A.capacity, copy the pre-A.size elements of B to A and run uninitialized_copy to copy the post-B.size -a.size elements of B to A
  • Reserve, apply for new memory, reverse copy all elements and free old memory
  • The implementation of assign(n, val) also has three cases
    • N > a.capacity, construct a new vector(suppose TMP) and execute a.swap(TMP)

    • A.size < n < a.capacity, same as operator=

    • N < a.size, same as operator=