Problem with reallocating array during runtime in Cpp

84 Views Asked by At

I am trying to create an array data structure in C++ using classes. The idea is that the array has a predefined length to it and whenever there is need for extra space, the array gets reallocated to double it's previous length. But, there is something wrong with the code that I am unable to figure out. It displays garbage values for items that should have been allocated.

Following is the relevant code:

#include <iostream>
using namespace std;

class Array
{
    private:
        int length = 8;
        int occupied_length = 0;
        int* array = new int[length];

        void reallocate_array()
        {
            array = (int*) realloc(array, length*2);
            length = length * 2;
        }
    public:
        Array()
        {
            cout << "How many elements do you want in the array: ";
            cin >> occupied_length;
            
            if (occupied_length > length) // In case array is full, it needs to be reallocated
                reallocate_array();

            for (int i = 0; i < occupied_length; i++)
                cin >> *(array + i);
            Print();
        }
        ~Array() { delete array; }
        void insert(int pos, int data)
        {
            if (pos > occupied_length) {cout << "Invalid Position!\n"; return; }

            occupied_length += 1;
            if (occupied_length >  length) { reallocate_array(); }

            if (pos == occupied_length){ *(array+occupied_length) = data; return;}
    
            for(int i = occupied_length-2; i >= pos-1; i--)
                *(array+i+1) = *(array+i);
            *(array + pos - 1) = data;
        }
        int size()
        {
            return occupied_length;
        }
        void Print()
        {
            for(int i =0; i <occupied_length; i++) { cout << *(array+i) << " "; }
            cout << endl;
        }
};

int main()
{
    Array arr;
    arr.insert(7,45);
    arr.Print();
    arr.insert(3,35);
    arr.Print();
    arr.insert(4,86);
    arr.Print();
    cout << arr.size() << endl;
    return 0;
}

The output of the above program is as follows: enter image description here

On top of that, the program does not immediately stop execution after the last cout statement. It waits for a second or two.

1

There are 1 best solutions below

1
Chris On

To sum up what comments are saying:

  • Don't mix malloc/calloc/realloc and new/delete. Undefined behavior awaits.
  • When using realloc the second argument is the number of bytes, so you would need to multiply by sizeof(int). But again, don't use realloc.
  • You will need to follow the rule of three/five/zero when implementing a class which manages its own memory, or double frees and other hijinks will ensue.

You might as well make this a templated class to avoid repeating yourself.

template <
    typename T,
    std::size_t INIT_SZ=8,
    typename GROW_BY=std::size_t,
    GROW_BY GROWTH_FACTOR=2
>
class Array {
    using self_type = Array<T, INIT_SZ, GROW_BY, GROWTH_FACTOR>;

    std::size_t allocated_size;
    std::size_t occupied_size;
    T *array;

    void grow() {
        std::size_t new_size = static_cast<std::size_t>(allocated_size * GROWTH_FACTOR);
        if (new_size == 0) new_size = INIT_SZ;

        T *new_array = new T[new_size];

        std::copy(array, array+occupied_size, new_array);

        delete[] array;

        array = new_array;
        allocated_size = new_size;
    }

public:
    // Constructor
    Array()
    : allocated_size(INIT_SZ),
      occupied_size(0),
      array(new T[INIT_SZ])
    { }

    // Destructor
    ~Array() { delete[] array; }

    // Copy constructor
    Array(const self_type& other)
    : allocated_size(other.allocated_size),
      occupied_size(other.occupied_size),
      array(new T[allocated_size])
    {
        std::copy(other.array, other.array + occupied_size, array);
    }

    // Copy assignment
    self_type& operator=(const self_type& other) {
        if (this == &other) return *this;

        delete[] array;

        allocated_size = other.allocated_size;
        occupied_size = other.occupied_size;

        std::copy(other.array, other.array + other.occupied_size, array);

        return *this;
    }

    // Move constructor
    Array(self_type&& other)
    : allocated_size(other.allocated_size),
      occupied_size(other.occupied_size),
      array(other.array)
    {
        other.occupied_size = 0;
        other.allocated_size = INIT_SZ;
        other.array = nullptr;
    }

    // Move assignment
    self_type& operator=(self_type&& other) {
        allocated_size = other.allocated_size;
        occupied_size = other.allocated_size;
        array = nullptr;

        std::swap(array, other.array);

        return *this;
    }

    void push_back(T val) {
        if (occupied_size == allocated_size) grow();

        array[occupied_size++] = val;
    }
};

Of course, with the contents private you'll need to implement public member functions to gain access to the data and size. You'll also want to implement iterators.