This paper is based on STL vector source code, but does not consider allocator allocator, iterator iterator, exception handling try/catch and so on, and _ucopy (), _umove (), _ufill () function will not be overly analyzed. I. Definition of vector
Template<class _ty,
class _ax>
class vector
: Public _vector_val<_ty, _ax>
{ // Varying size array of values public
:
/********/
protected:
pointer _myfirst; Pointer to beginning of array
pointer _mylast; Pointer to the sequence
pointer _myend;//Pointer to end of array
};
The simple understanding is that vector is represented by the three pointers above, and the basic schematic diagram is as follows:
Two key sizes:
Size: Size=_mylast-_myfirst;
Capacity: Capacity=_myend-_myfirst;
Corresponds to resize (), reserve () two functions respectively.
The size represents the number of elements that are already in the vector, and the capacity represents the number of elements that the vector can store; In order to reduce the cost of two allocations, the vector's actual size may be larger than the customer's needs for future expansion, which is the concept of capacity. That is capacity>=size, when equal, the container is full at this time, if you want to add new elements, the memory allocation, the entire vector of data to move to new memory. The secondary allocation cost is high, in the actual operation, should try to reserve a certain space, avoid two times distribution. ii. structure and deconstruction
1. Construction
Vector constructors are mainly of the following types:
Vector (): _mybase ()
{ //construct empty vector
_buy (0);
}
Explicit vector (Size_type _count): _mybase ()
{ //construct from _count * _ty () _construct_n (
_count, _ty ()) ;
}
Vector (Size_type _count, const _ty& _val): _mybase ()
{ //construct from _count * _val
_construct_n (_ Count, _val);
}
Vector (const _myt& _right): _mybase (_right._alval)
{ //construct by copying _right
if (_buy (_ Right.size ())
_mylast = _ucopy (_right.begin (), _right.end (), _myfirst);
One of the secrets of the vector's excellent performance is to configure more memory than the elements it holds, and generally reserve enough space to avoid two allocations before using vector, which makes the vector performance best. So the number of elements _count is a much more important parameter than the element value _val, so when constructing a vector, the first parameter must be the number of elements.
It is known from the above constructors that basically all constructors are based on _construct _n ()
BOOL _buy (Size_type _capacity)
{ //allocate array with _capacity elements _myfirst =
0, _mylast = 0, _myend = 0;
if (_capacity = 0) //_count is 0 o'clock, return directly
(false);
else
{ //nonempty array, allocate storage
_myfirst = this->_alval.allocate (_capacity); Allocate memory and update member variable
_mylast = _myfirst;
_myend = _myfirst + _capacity;
}
return (TRUE);
}
void _construct_n (Size_type _count, const _ty& _val)
{ //construct container if (_count) containing _val-valued elements _buy
(_count) )
_mylast = _ufill (_myfirst, _count, _val);
This completes the construction of the vector container.
2, destructor
The vector destructor is simple, destroying all existing elements and freeing all memory
void _tidy ()
{ //FREE all storage
if (_myfirst!= 0)
{ //something to free, destroy and deallocate I T
_destroy (_myfirst, _mylast);
This->_alval.deallocate (_myfirst, _myend-_myfirst);
}
_myfirst = 0, _mylast = 0, _myend = 0;
}
iii. inserting and deleting elements
Vector insertion and deletion elements are implemented through the Push_ back (), Pop_back () two interfaces, and their internal implementations are very simple
void push_back (const _ty& _val)
{ //Insert element at end
if (size () < capacity ())
_mylast = _ Ufill (_mylast, 1, _val);
else
Insert (end (), _val); With insufficient space, it triggers two allocations of memory
}
void Pop_back ()
{ //erase element at end
if (!empty ())
{ / /Erase last element
_destroy (_mylast-1, _mylast);
--_mylast
}
}
Iv. Other Interfaces
1. Reserve () operation
Previously mentioned the reserve (count) function is primarily reserved for the size of the count space, corresponding to the capacity of the container, the purpose is to ensure (_myend-_myfirst) >=count. It only operates when there is not enough space to redistribute a piece of memory, copy the original elements to the new memory, and destroy the original memory
void Reserve (Size_type _count)
{ //determine new minimum length of allocated storage
if (capacity () < _cou NT)
{ //Not enough room, reallocate
pointer _ptr = this->_alval.allocate (_count);
_umove (Begin (), End (), _ptr);
Size_type _size = Size ();
if (_myfirst!= 0)
{ //Destroy and deallocate old array
_destroy (_myfirst, _mylast);
This->_alval.deallocate (_myfirst, _myend-_myfirst);
}
_myend = _ptr + _count;
_mylast = _ptr + _size;
_myfirst = _ptr;
}
2, resize () operation
The Resize (Count) function is primarily used to change the size of a vector, which in turn changes the value of (_mylast-_myfirst) and inserts the element when size < Count, when size is >count, Erases the element.
void Resize (Size_type _newsize, _ty _val)
{ //Determine new length, padding with _val elements as needed
if ( Size () < _newsize)
_insert_n (End (), _newsize-size (), _val);
else if (_newsize < size ())
Erase (Begin () + _newsize, End ());
}
3, _insert_n () operation
The resize () operation and the Insert () operation take advantage of the _insert_n () function, which is very important and slightly more complex than the other functions.
Although _insert_n (_where, _count, _val) function is relatively long, but the operation is very simple, mainly can be divided into the following situations:
1, _count = = 0, do not need to insert, direct return
2, Max_size ()-size () < _count, exceeding the maximum capacity of the system setup, will overflow, resulting in Xlen () exception 3, _capacity < size () + _count,vector capacity is not enough to insert the Count element. Two allocations are required to enlarge the capacity of the vector. Under VS, the vector capacity will expand by 50%, i.e. _capacity = _capacity + _capacity/2;
If still insufficient, then _capacity = size () + _count;
else if (_capacity < size () + _count)
{ //Not enough room, reallocate
_capacity = max_size ()-_capacity/ 2 < _capacity
0: _capacity + _capacity/2; Try to grow by 50%
if (_capacity < size () + _count)
_capacity = size () + _count;
Pointer _newvec = This->_alval.allocate (_capacity);
Pointer _ptr = _newvec;
_ptr = _umove (_myfirst, _vec_iter_base (_where), _newvec); Copy prefix
_ptr = _ufill (_ptr, _count, _val); Add New Stuff
_umove (_vec_iter_base (_where), _mylast, _ptr); Copy suffix
//memory release and variable update
}
In this case, the data is moved from the original container to the newly allocated memory, from the previous to the back.
4, enough space, and the position of the inserted element is closer to _mylast, that is, the tail of the existing element
In this case, there is no need to allocate memory again, and the data is operated backwards. The first is to move the where~last backwards, to reserve the count size space for the data to be inserted, to start the fill from the _mylast, and then to populate the remaining elements from where
else if ((Size_type) (_mylast-_vec_iter_base (_where)) < _count)
{ //new stuff spills off end
_umove (_vec _iter_base (_where), _mylast,
_vec_iter_base (_where) + _count); Copy suffix
_ufill (_mylast, _count-(_mylast-_vec_iter_base (_where)),
_val); Insert new stuff off end
_mylast + + = _count;
Std::fill (_vec_iter_base (_where), _mylast-_count,
_val); Insert up to old end
}
5, the space is sufficient, but the insertion position compares the front
{ //new stuff can all is assigned
_ty _tmp = _val; In case _val are in sequence
pointer _oldend = _mylast;
_mylast = _umove (_oldend-_count, _oldend,
_mylast); Copy suffix
_stdext _unchecked_move_backward (_vec_iter_base (_where), _oldend-_count, _oldend
); Copy hole
Std::fill (_vec_iter_base (_where), _vec_iter_base (_where) + _count, _tmp
); Insert INTO Hole
}
4, Erase () operation
Iterator Erase (const_iterator _first_arg,
const_iterator _last_arg)
{ //erase [_first, _last]
Iterator _first = _make_iter (_first_arg);
Iterator _last = _make_iter (_last_arg);
if (_first!= _last)
{ //worth doing, copy down-over hole pointer
= _ptr _stdext (unchecked_copy E (_last), _mylast,
_vec_iter_base (_first));
_destroy (_ptr, _mylast);
_mylast = _ptr;
}
return (_first);
}
The main operation is to copy the valid elements of the latter part forward, to deconstruct the invalid elements in the back space, and to update the _MYLAST variable
5, assign () operation
The Assign () operation eventually calls to the following function, which first erases all elements already in the container and inserts the count Val element from scratch
void _assign_n (Size_type _count, const _ty& _val)
{ //Assign _count * _val _ty = _tmp _val
; In case _val
are in sequence erase (begin (), end ());
Insert (Begin (), _count, _tmp);
V. Basic use
After the above analysis of vector internal implementation, it becomes much simpler to understand the corresponding interface.
Vector external interface can be divided into: construction, deconstruction:
ector<elem> c
Vector <Elem> C1 (c2)
vector <Elem> C (n)
vector <Elem> C (n, Elem)
vector <Elem> C (beg,end)
c.~ vector <Elem> ()
Inserts, deletes, assigns a value
C.push_back (elem)
c.pop_back () C.insert (Pos,elem) C.insert (Pos,n,elem) c.insert (Pos,beg
, End)
c.erase (POS)
c.erase (beg,end)
c.clear ()
c.assign (beg,end)
c.assign (N,elem)
Size related
C.capacity ()
c.max_size ()
c.resize (num)
c.reserve ()
c.size ()
Get iterators
C.begin ()
c.end ()
c.rbegin ()
c.rend ()
Get Data
Operator[]
c.at (idx)
C.front ()
c.back ()