dimanche 5 juillet 2015

How to realloc in c++?

The following code constitutes a MCVE, this reproduces the problem I want to ask about but it's not the real code. The real code is quite more complicated so that's why I wrote this for a demonstration of the problem.

The key feature I am looking for is to be able to grow a dynamically allocated array, please do not suggest using the stl because it's explicitly forbidden. This code is for educational purpose and thus there are restrictions.

#include <cstring>
#include <iostream>

class Value
{
    public:
        Value(int value = 0);
        Value(const Value &value);
        Value &operator =(const Value &other);
        ~Value();

        operator int() {return *m_absurdPointer;}

    private:
        int *m_absurdPointer;
};

Value::Value(int value) :
    m_absurdPointer(new int[1])
{
    *m_absurdPointer = value;
}

Value::Value(const Value &value)
{
    m_absurdPointer = new int[1];
    memcpy(m_absurdPointer, value.m_absurdPointer, sizeof(*m_absurdPointer));
}

Value &Value::operator =(const Value &other)
{
    m_absurdPointer = new int[1];
    memcpy(m_absurdPointer, other.m_absurdPointer, sizeof(*m_absurdPointer));

    return *this;
}

Value::~Value()
{
    delete[] m_absurdPointer;
}

class ValueArray
{
    public:
        ValueArray();
        ~ValueArray();

        void append(const Value &value);
        void show() const;

    private:
        Value *m_array;
        unsigned int m_capacity;
        unsigned int m_length;
};


ValueArray::ValueArray() :
    m_array(nullptr)
  , m_capacity(0)
  , m_length(0)
{
}

ValueArray::~ValueArray()
{
    delete[] m_array;
}

void
ValueArray::append(const Value &value)
{
    if (m_length >= m_capacity)
    {
        Value *newarray;
        unsigned int unitSize;

        unitSize = 1;       
        newarray = new Value[m_capacity + unitSize];

        if ((m_capacity > 0) && (m_array != nullptr))
            memcpy(newarray, m_array, m_capacity * sizeof(*m_array));
        delete[] m_array;

        m_array = newarray;
        m_capacity += unitSize;
    }
    m_array[m_length++] = value;
}

void
ValueArray::show() const
{
    for (size_t i = 0 ; i < m_length ; ++i)
        std::cout << static_cast<int>(m_array[i]) << std::endl;
}

int
main(void)
{
    ValueArray example;

    for (int i = 0 ; i < 10 ; ++i)
        example.append(Value(i));
    example.show();

    return 0;
}

It causes as you can see a double free issue, because the delete[] m_array; calls the destructor of the class Value after it has copied the values to the re-newed array.

I tried to do this with malloc()/realloc() but I need the destructor of Value() to be called so new is mandatory because I can't use free().

How to prevent this?, if I remove the delete[] m_absurdPointer; the double free would be gone of course but there would be a memory leak.

Aucun commentaire:

Enregistrer un commentaire