davidalk
davidalk

Reputation: 105

free(): double free detected in tcache 2

I'm writing my own dynamic array class in C++ (similarly to std::vector), and I'm running into a problem when having a dynamic array containing dynamic arrays.

Basically when having an array of all data types (int, double, float, std::string etc.) there is no problem and all the functionalities of the class works great.

When the data type is another array though something messes up and there is an error raising in the end of the program (free(): double free detected in tcache 2)

All of the code:

DynamicArray.h:

#pragma once

#include <iostream>

namespace Utils
{
    template <typename T>
    class DynamicArray
    {
        private:
            size_t array_length;
            T* array;
        public:
            ~DynamicArray();
            DynamicArray();
            DynamicArray(const int& initialLength);
            void Print();
            size_t GetLength() const;
            void AddItem(const T& newItem);
            // TODO: void AddItems(const T* newItemsArray);
            void RemoveItem(int index);
            T& GetItem(int index);
            void SetItem(const int& index, const T& newValue);
            T& operator [](int index) const;
            void ResetArray(T resetValue);
    };
}

#include "DynamicArray.cpp"

DynamicArray.cpp:

#include "DynamicArray.h"

template<typename T>
Utils::DynamicArray<T>::~DynamicArray()
{
    std::cout << "before del" << this->array_length << "\n";
    if (this->array_length > 0)
        delete[] this->array;
    std::cout << "after del\n";
}

template<typename T>
Utils::DynamicArray<T>::DynamicArray()
{
    this->array_length = 0;
}

template<typename T>
Utils::DynamicArray<T>::DynamicArray(const int& initialLength)
{
    this->array_length = initialLength;
    T* new_array = new T[initialLength];
    this->array = new_array;
}

template<typename T>
void Utils::DynamicArray<T>::Print()
{
    for (size_t i = 0; i < this->array_length; i++)
        std::cout << this->array[i] << std::endl;
}

template<typename T>
size_t Utils::DynamicArray<T>::GetLength() const
{
    return this->array_length;
}

template<typename T>
void Utils::DynamicArray<T>::AddItem(const T& newItem)
{
    T* new_array = new T[this->array_length + 1];

    for (size_t i = 0; i < this->array_length; i++)
        new_array[i] = this->array[i];

    new_array[array_length] = newItem;

    // Releasing the memory of array
    if (this->array_length != 0)
    {
        delete[] this->array;
        this->array = nullptr;
    }

    this->array_length += 1;
    this->array = new_array;
}

template<typename T>
void Utils::DynamicArray<T>::RemoveItem(int index)
{
    T* new_array = new T[this->array_length - 1];

    int temp_index = 0;
    for (size_t i = 0; i < this->array_length; i++)
    {
        if (i != index)
        {
            new_array[temp_index] = this->array[i];
            temp_index++;
        }
    }

    // Releasing the memory of array
    delete[] this->array;
    this->array = nullptr;

    this->array_length -= 1;
    this->array = new_array;
}

template <typename T>
T& Utils::DynamicArray<T>::GetItem(int index)
{
    return this->array[index];
}

template<typename T>
T& Utils::DynamicArray<T>::operator[](int index) const
{
    return this->array[index];
}

template <typename T>
void Utils::DynamicArray<T>::ResetArray(T resetValue)
{
    for (int i = 0; i < this->array_length; i++)
        this->array[i] = resetValue;
}

template <typename T>
void Utils::DynamicArray<T>::SetItem(const int& index,const T& newValue)
{
    this->array[index] = newValue;
}

main function:

#include <iostream>
#include "DynamicArray.h"

int main()
{
    Utils::DynamicArray<Utils::DynamicArray<double>> outputs;
    Utils::DynamicArray<double> singleOutput;
    singleOutput.AddItem(1);
    singleOutput.AddItem(1);
    outputs.AddItem(singleOutput);
}

Output given when running the program:

before del2
after del
before del1
before del2
free(): double free detected in tcache 2
Aborted (core dumped)

Any ideas? No matter what I tried nothing worked..

Upvotes: 3

Views: 16148

Answers (1)

Chris Uzdavinis
Chris Uzdavinis

Reputation: 6131

You failed to write proper copy constructor and assignment operators:

DynamicArray(DynamicArray const& rhs); // copy constructor
DynamicArray& operator=(DynamicArray const& rhs); // copy assignment

When you don't write these yourself, they are generated with shallow copy semantics. Since your class "owns" a pointer, if you shallow copy it, then two instances o DynamicArray both own the same pointner, and when one is destroyed, it destroys the data pointed to by the other. And when the other is destroyed, you get a double free.

To write these you need to allocate memory and do a full copy.

(You also would eventually want to write a move constructor, and move assignment operator.)

The element declared on the stack in main() is also copied into the other DynamicArray. The double free occurs when the stack of main is cleaned up: first delete is in the destructor of singleOutput, and the second delete is in the destructor of outputs, which holds an element that has the same pointer as singleOutput.

You also leave your "array" member uninitialized in the default constructor. That does not set it to zero, it leaves garbage in it. (Which could be zero, but might not be.)

Upvotes: 8

Related Questions