Ramy Al Zuhouri
Ramy Al Zuhouri

Reputation: 21976

std::allocator not preserving old allocated items after reallocation

I am trying to realize a Vector class that allocates a block of memory, and eventually re-allocate it if it needs to contain more items.
I am using std::allocator to do this:

#include <iostream>
#include <stdexcept>

using namespace std;

template <class T>
class Vector
{
private:
    T* data;
    allocator<T> data_all;
    int length;
    int _size;
    static const int block_size=10;
    void init()
    {
        length=0;
        _size=block_size;
        data=data_all.allocate(block_size,NULL);
    }
public:
    Vector()
    {
        init();
    }
    int size() const
    {
        return length;
    }
    void push_back(T item)
    {
        length++;
        if(length > _size)
        {
            _size+=block_size;
            data=data_all.allocate(_size,data);
        }
        data_all.construct(&data[length-1],item);
    }
    T& operator[] (int i)
    {
        if(i<0 || i>= length)
            throw out_of_range("The index is out of vector range");
        return data[i];
    }
};

int main(int argc, char** argv)
{
    Vector<int> v;
    for(int i=0; i<20; i++)
        v.push_back(i);
    for(int i=0; i<v.size(); i++)
        cout << v[i] << "\t";
    return 0;
}

The problem is that the item previously allocated are not preserved, it prints:

0   0   0   0   0   0   0   0   0   0   10  11  12  13  14  15  16  17  18  19  

Instead of:

0   1   2   3   4   5   6   7   8   9   10  11  12  13  14  15  16  17  18  19  

Why this behaviour? Isn't there a way in C++ to re-allocate contiguous items like in C with realloc?

Upvotes: 3

Views: 157

Answers (1)

Jonathan Wakely
Jonathan Wakely

Reputation: 171333

The second argument to allocate is just a hint that the allocator could use to try and return new memory close to the old memory, but is ignored by std::allocator and fairly useless for vector-like containers because all the elements are close to each other anyway, as they're in a contiguous block.

You seem to be expecting it to copy the existing data. It won't. You have to do that, by copying from the old memory block to the new one.

You're also leaking the old memory. You need to deallocate it.

You want something like:

void push_back(const T& item)
{
    if (length == _size)
    {
        T* new_data = data_all.allocate(_size+block_size);
        // N.B. if any of the following copies throws new_data will be leaked
        std::uninitialized_copy(data, data+length, new_data);
        std::destroy(data, data+length);
        data_all.deallocate(data, _size);
        data = new_data;
        _size+=block_size;
    }
    data_all.construct(&data[length++],item);
}

Upvotes: 3

Related Questions