c++memorydynamic-memory-allocation

Segmentation fault on copy custom dynamic array


I'm trying to implement my custom dynamic array, like std::vector.

Dynamic array module code:

#ifndef ARRAY_H
#define ARRAY_H

#include <cstdlib>

#define CAPACITY_SCALE_FACTOR 2

template <typename T>
struct Array
{
  T*      data;
  size_t  size;
  size_t  capacity;

  Array()
  {
    size     = 0;
    capacity = 10;
    data = (T*)malloc(capacity * sizeof(T));
  }

  ~Array()
  {
    free(data);
    data = NULL;
    size = capacity = 0;
  }

  T& operator[](size_t index);
  const T& operator[](size_t index) const;

  Array(const Array &other){
    if (array_size(other))
      for (size_t i = 0; i < other.size; i++)
        array_insert(*this, other[i]);
  }

  Array& operator=(const Array& other) {
    if (array_size(other))
      for (size_t i = 0; i < array_size(other); i++)
        array_insert(*this, other[i]);

    return *this;
  }
};


template <typename T>
T& Array<T>::operator[](size_t index)
{
  return data[index];
}

template <typename T>
const T& Array<T>::operator[](size_t index) const
{
  return data[index];
}

template <typename T>
size_t array_size(const Array<T> &array)
{
  return array.size;
}

template <typename T>
void array_insert(Array<T> &array, const T value)
{
  array[array.size] = value;

  array.size++;

  if (array.size == array.capacity)
  {
    array.capacity *= CAPACITY_SCALE_FACTOR;
    array.data = (T*)realloc(array.data, array.capacity * sizeof(T));
  }
}

template <typename T>
void array_delete(Array<T> &array, size_t index)
{
  for (size_t i = index; i < array.size - 1; i++)
  {
    array[i] = array[i + 1];
  }

  array.size--;

  if ( array.size <= ( array.capacity / (CAPACITY_SCALE_FACTOR * 2) ) )
  {
    array.capacity =
      (array.size == 0) ? 1 : array.size * CAPACITY_SCALE_FACTOR;

    array.data = (T*)realloc(array.data, array.capacity * sizeof(T));
  }
}

#endif

Usage example code:

#include "array.h"

int main()
{
  Array<char> one = Array<char>();
  array_insert(one, 'C');
  Array<char> two = one;

  return 0;
}

When I try to assign one array to another Array<char> two = one; I receive Segmentation fault (core dumped). I tried to resolve this issue by implemented copy constructor and copy assign operator, but it didn't help.


Solution

  • As mentioned in comments, you did not implement your copy constructor and assignment operator correctly, as you are not initializing your class members before using them. Also, you should not be using malloc()/realloc() at all in C++, as they do not manage non-trivial types correctly. Use new[] and delete[] instead.

    Also, in your array_insert(), you are growing the array after adding a new item into it, but you should grow before adding instead.

    Also, consider using the Copy/Swap Idiom, and standard library functions, to simplify your coding.

    Try something more like this:

    #ifndef ARRAY_H
    #define ARRAY_H
    
    #include <utility>
    #include <algorithm>
    
    template <typename T>
    struct Array
    {
    private:
      static const size_t CAPACITY_SCALE_FACTOR = 2;
    
      T*      m_data;
      size_t  m_size;
      size_t  m_capacity;
    
      void swap(Array &other, bool swap_size = true)
      {
        std::swap(m_data,     other.m_data);
        std::swap(m_capacity, other.m_capacity);
        if (swap_size) {
          std::swap(m_size,   other.m_size);
        }
      }
    
    public:
      Array(size_t initial_capacity = 10)
      {
        m_size     = 0;
        m_capacity = initial_capacity;
        m_data     = (m_capacity > 0) ? new T[m_capacity] : nullptr;
      }
    
      Array(const Array &other)
        : Array(other.m_size)
      {
        std::copy_n(other.m_data, other.m_size, m_data);
        m_size = other.m_size;
      }
    
      ~Array()
      {
        delete[] m_data;
      }
    
      Array& operator=(const Array& other)
      {
        if (this != &other) {
          Array(other).swap(*this);
        }
        return *this;
      }
    
      T& operator[](size_t index) { return m_data[index]; }
      const T& operator[](size_t index) const { return m_data[index]; }
    
      size_t size() const { return m_size; }
      size_t capacity() const { return m_capacity; }
    
      void insert(const T &value)
      {
        if (m_size == m_capacity)
        {
          Array<T> temp(m_capacity * CAPACITY_SCALE_FACTOR);
          std::copy_n(m_data, m_size, temp.m_data);
          temp.swap(*this, false);
        }
        m_data[m_size] = value;
        ++m_size;
      }
    
      void remove(size_t index)
      {
        if (index >= m_size) {
          return; // or better, throw an exception, like std::out_of_range...
        }
    
        std::copy(m_data + index + 1, m_data + m_size, m_data);
        --m_size;
        m_data[m_size] = T(); // clear unused data
    
        if (m_size <= (m_capacity / (CAPACITY_SCALE_FACTOR * 2)))
        {
          Array temp(m_size * CAPACITY_SCALE_FACTOR);
          std::copy_n(m_data, m_size, temp.m_data);
          temp.swap(*this, false);
        }
      }
    };
    
    #endif
    

    Online Demo