C ++ Primer study note _ 47_STL Analysis (2): vector source code analysis, memory distributor Allocator
1. Source Code of vector initialization: Track A simple program, observe the initialization process of vector, and debug the single-step execution.
#include
#include
using namespace std;int main(){ vector
v; return 0;}
1. First, the code is executed.
vector() _NOEXCEPT : _Mybase() { // construct empty vector } explicit vector(const _Alloc& _Al) _NOEXCEPT : _Mybase(_Al) { // construct empty vector, allocator }2. Execute allocator
allocator() _THROW0() { // construct default allocator (do nothing) }3. Further execution
_Vector_alloc(const _Alloc& _Al = _Alloc()) : _Mypair(_One_then_variadic_args_t(), _Al) { // construct allocator from _Al _Alloc_proxy(); }4. allocate memory. The Initialization is 0. After a series of calls, the initialization is complete.
_Vector_val() { // initialize values _Myfirst = pointer(); _Mylast = pointer(); _Myend = pointer(); } pointer _Myfirst; // pointer to beginning of array pointer _Mylast; // pointer to current end of sequence pointer _Myend; // pointer to end of array };Ii. capacity
1. First, the implementation of vector in VC 2008 is complicated, although the Declaration of vector is consistent with that of VC6.0, as follows:
Template <class _ Ty, class _ Ax = allocator <_ Ty> // The second parameter is the class vector with the default parameter;
2. However, in VC2008, vector also has a base class, as shown below:
// TEMPLATE CLASS vectortemplate < class _Ty, class _Ax >class vector : public _Vector_val<_Ty, _Ax>{};
3. Let's take a look at the base class _ Vector_val:
// TEMPLATE CLASS _Vector_valtemplate < class _Ty, class _Alloc >class _Vector_val : public _CONTAINER_BASE_AUX_ALLOC<_Alloc>{ // base class for vector to hold allocator _Alvalprotected: _Vector_val(_Alloc _Al = _Alloc()) : _CONTAINER_BASE_AUX_ALLOC<_Alloc>(_Al), _Alval(_Al) { // construct allocator from _Al } typedef typename _Alloc::template rebind<_Ty>::other _Alty; _Alty _Alval; // allocator object for values};
4. To understand the type of _ Alty, you have to take a look at the allocator template class:
template
class allocator{ template<> class _CRTIMP2_PURE allocator
{ // generic allocator for type void public: template
struct rebind { // convert an allocator
to an allocator <_Other> typedef allocator<_Other> other; }; .... }; ...};
Typedeftypename_Alloc: templaterebind <_ Ty>: other_Alty; The type definition is used as a whole. Suppose we use vector now. , Then _ Ty is int, _ Ax is allocator When the vector class is passed to the base class Vector_val, _ Alloc is allocator. ; You can see allocator Is the feature of allocator template class, rebind <_ Ty> is the member template class, other is the custom type of the member template class, And _ Ty is the int type, then the other type is also allocator That is, _ Alty is of the type allocator. . _ Alty _ Alval; that is, the base class defines a allocator Type Member, which is inherited by vector and used to allocate memory for elements in the vector.
For example, iteratornew_data = alloc. allocate (new_size); note that the standard vector: iterator is implemented by the template class. The following implementation simply treats it as a pointer, in fact, the real implementation of the iterator class is that there is a pointer member inside, pointing to the container element.
××××××××××××××××××××××××××××××××××××××××××× ××××××××××××××××××××××××××××××××××××××××××× *
Compared with the implementation of list, it inherits a member of its base class _ List_nod allocator <_ Node> _ Alnod; as follows:
Typename _ Alloc: template rebind <_ Node>: other _ Alnod; // allocator object for nodes
Among them, _ Node has three members:
_ Nodeptr _ Next; // successor node, or first element if head
_ Nodeptr _ Prev; // predecessor node, or last element if head
_ Ty _ Myval; // the stored value, unused if head
For list , Then _ Ty is the int type. When creating a new node, the two-way linked list is implemented as follows:
_ Nodeptr _ Pnode = this-> _ Alnod. allocate (1); // allocates the space of a node and returns a pointer to the node.
In fact, list also inherits two members of the other two base classes, as follows:
Typename _ Alloc: template rebind <_ Nodeptr>: other _ Alptr; // allocator object for pointers to nodes
Typename _ Alloc: template rebind <_ Ty>: other _ Alty _ Alval; // allocator object for values stored in nodes
××××××××××××××××××××××××××××××××××××××××××× ××××××××××××××××××××××××××××××××××××××××××× *
3. How to Implement continuous space in a vector Dynamic Array
1. Track A simple program, observe the capacity allocation process of the vector, and debug the single-step execution.
#include
#include
using namespace std;int main(){ vector
v; v.push_back(1); cout << v.capacity() << endl; v.push_back(1); cout << v.capacity() << endl; v.push_back(1); cout << v.capacity() << endl; v.push_back(1); cout << v.capacity() << endl; v.push_back(1); cout << v.capacity() << endl; v.push_back(1); cout << v.capacity() << endl; v.push_back(1); cout << v.capacity() << endl; v.push_back(1); cout << v.capacity() << endl; return 0;}
Capacity is calculated as follows: capacity increases to the original capacity + the original capacity/2;
The increasing source code tracking results are as follows:
size_type _Grow_to(size_type _Count) const { // grow by 50% or at least to _Count size_type _Capacity = capacity(); _Capacity = max_size() - _Capacity / 2 < _Capacity ? 0 : _Capacity + _Capacity / 2; // try to grow by 50% if (_Capacity < _Count) _Capacity = _Count; return (_Capacity); }
2. The concept of capacity and vector size is different. capacity> = size, as shown in:
Size refers to the interval of _ Mylast-_ Myfirst; capacity refers to the interval of _ Myend-_ Myfirst; that is, there is no space available.
When push_back is usually followed by multiple copy and destructor operations, a large size () space capacity can be split at once to reduce the efficiency problems caused by frequent operations.
Generally, the vector caches a portion of the memory space to accommodate more elements. In this way, when a new element is inserted, the memory does not need to be re-allocated, improving the insertion speed.
Iv. Memory distributor Allocator
Allocator template class:
#include
template
class allocator{public: T *allocate(size_t); void deallocate(T *, size_t); void construct(T *, size_t); void destroy(T *); //.......};
Of course, the actual interface is not that simple to implement, but the functions are roughly the same:
Allocate calls operator new; deallocate calls operator delete; construct calls placement new (that is, the copy constructor is called on the allocated memory), and destroy calls the destructor.
Refer:
C ++ primer version 4
Valid tive C ++ 3rd
C ++ programming specifications