Search code examples
c++push-back

Implementation of push_back and pop_back


I want to understand how std::vector<T>::push_back and std::vector<T>::pop_back create and destroy objects in an allocated memory ?

I used google and all I find is people just play with size and capacity to limit the access to the internal dynamic array but I don't think that's how things really work in the standard implementation

Note: I'm not asking for the standard implementation because it would be complex but I would appreciate a basic implementation for this approach


EDIT: I figured out how to implement my own custom allocator

For simplicity I'll show only important functions out of my custom allocator

template <typename T>
T* allocate(std::size_t count) {
    return static_cast<T*>(::operator new(count * sizeof(T)));
}

template <typename T>
void deallocate(T* ptr, std::size_t count) {
    operator delete(ptr);
}


template <typename U, typename... Args>
void construct(U* ptr, Args&&... args) {
    new(ptr) U(std::forward<Args>(args)...);
}

template <typename U>
void destroy(U* ptr) {
    ptr->~U();
}

then I use then in my own defined vector something like this

int* buff = allocate<int>(8);
// This is like:
// std::vector<int> vec;
// vec.reserve(8);

int* last = &buff[0];
construct<int>(last, 32);
// This is like:
// vec.push_back(32);

++last;
construct<int>(last, 12);
// This is another push
// vec.push_back(12);

destroy(last);
--last;
// This is like: 
// vec.pop_back();


deallocate(buff, 8);
// This shoud be in:
// ~vector();

Please check it out if missed something ... thanks


Solution

  • All standard containers with an allocator are using the allocator to construct or destroy elements:

    23.2.1 [3] General container requirements (N4296)

    For the components affected by this subclause that declare an allocator_type, objects stored in these components shall be constructed using the allocator_traits::construct function and destroyed using the allocator_traits::destroy function

    The default allocator in the standard library is using placement new to construct and invokes the destructor to destroy an element:

    20.7.9 [11] and [12] The default allocator (N4296)

    template <class U, class... Args>
    void construct(U* p, Args&&... args);
    

    Effects: ::new((void *)p) U(std::forward(args)...)

    template <class U>
    void destroy(U* p);
    

    Effects: p-> ~ U()