How is dynamic memory managed in std::vector?

后端 未结 4 1509
南笙
南笙 2021-01-03 08:52

How does std::vector implement the management of the changing number of elements: Does it use realloc() function, or does it use a linked list?

Thanks.

4条回答
  •  别那么骄傲
    2021-01-03 09:32

    It uses the allocator that was given to it as the second template parameter. Like this then. Say it is in push_back, let t be the object to be pushed:

    ...
    if(_size == _capacity) { // size is never greater than capacity
        // reallocate
        T * _begin1 = alloc.allocate(_capacity * 2, 0);
        size_type _capacity1 = _capacity * 2;
    
        // copy construct items (copy over from old location).
        for(size_type i=0; i<_size; i++)
            alloc.construct(_begin1 + i, *(_begin + i));
        alloc.construct(_begin1 + _size, t);
    
        // destruct old ones. dtors are not allowed to throw here. 
        // if they do, behavior is undefined (17.4.3.6/2)
        for(size_type i=0;i<_size; i++)
            alloc.destroy(_begin + i);
        alloc.deallocate(_begin, _capacity);
    
        // set new stuff, after everything worked out nicely
        _begin = _begin1;
        _capacity = _capacity1;
    } else { // size less than capacity
        // tell the allocator to allocate an object at the right
        // memory place previously allocated
        alloc.construct(_begin + _size, t);
    }
    _size++; // now, we have one more item in us
    ...
    

    Something like that. The allocator will care about allocating memory. It keeps the steps of allocating memory and constructing object into that memory apart, so it can preallocate memory, but not yet call constructors. During reallocate, the vector has to take care about exceptions being thrown by copy constructors, which complicates the matter somewhat. The above is just some pseudo code snippet - not real code and probably contains many bugs. If the size gets above the capacity, it asks the allocator to allocate a new greater block of memory, if not then it just constructs at the previously allocated space.

    The exact semantics of this depend on the allocator. If it is the standard allocator, construct will do

    new ((void*)(_start + n)) T(t); // known as "placement new"
    

    And the allocate allocate will just get memory from ::operator new. destroy would call the destructor

    (_start + n)->~T();
    

    All that is abstracted behind the allocator and the vector just uses it. A stack or pooling allocator could work completely different. Some key points about vector that are important

    • After a call to reserve(N), you can have up to N items inserted into your vector without risking a reallocation. Until then, that is as long as size() <= capacity(), references and iterators to elements of it remain valid.
    • Vector's storage is contiguous. You can treat &v[0] as a buffer containing as many elements you have currently in your vector.

提交回复
热议问题