динамический массив в c ++: утечка valgrind - PullRequest
0 голосов
/ 07 июня 2018

Я пытаюсь реализовать динамический массив в C ++.как только он заполняется, его размер удваивается и копируется в массив двойного размера.это выглядит хорошо, но Valgrind говорит, что у меня есть утечка.Я должен сказать, что мне не разрешено использовать любую из c ++ STL-структур - поэтому я использую операторы new [] и delete [].

это мой код:

template<class T>
class DynamicArray
{

    int size;
    int numOfElements;
    T* arr;

public:
    DynamicArray(int size) :
            size(size), numOfElements(0)
    {
        arr = new T[size];
    }
    ;

    int getSize()
    {
        return size;
    }

    int getNumberOfElements()
    {
        return numOfElements;
    }

    void insert(T element)
    {
        arr[numOfElements++] = element;
        if (numOfElements == size)
        {
            T* extended_array = new T[size * 2];
            for (int i = 0; i < numOfElements; i++)
            {
                extended_array[i] = arr[i];
            }
            delete[] arr;
            arr = extended_array;
            size = size * 2;
        }
    }

    T& operator[](int i)
    {
        if (!((i >= 0) && (i < size)))
            throw arrayOutOfBoundsException();
        return arr[i];
    }

    ~DynamicArray()
    {
        delete[] arr;
    }

    class arrayOutOfBoundsException: std::exception
    {
    };
};

Моя основная программа:

using std::cout;
using std::endl;

void printIntArray(DynamicArray<int> arr){
    cout << "size: " << arr.getSize() << ";  " << "numOfElemnts: " << arr.getNumberOfElements() << endl;
    cout << "array: ";
    for(int i=0; i<arr.getNumberOfElements(); i++){
        cout << " " << arr[i];
    }
    cout << endl << endl;
}


int main() {

    DynamicArray<int> arr(5);
    printIntArray(arr);



    arr.insert(1);
    arr.insert(2);
    arr.insert(3);
    printIntArray(arr);

    arr.insert(4);
    printIntArray(arr);

    arr.insert(5);
    printIntArray(arr);

    arr.insert(6);
    printIntArray(arr);

    arr.insert(7);
    arr.insert(8);
    arr.insert(9);
    printIntArray(arr);

    arr.insert(16);
    printIntArray(arr);

    arr[9] = 901;
    arr[0] = 101;
    printIntArray(arr);

** valgrind показывает мне эту ошибку - когда деструктор компилируется как комментарий:

==3954== 80 bytes in 1 blocks are definitely lost in loss record 1 of 1
==3954==    at 0x4A07192: operator new[](unsigned long) (vg_replace_malloc.c:363)
==3954==    by 0x40103E: DynamicArray<int>::insert(int) (DynamicArray.h:41)
==3954==    by 0x400DBC: main (main.cpp:44)**

Может кто-нибудь сказать мне, что я делал неправильно?я неправильно использую "delete []"?или деструктор?

спасибо

1 Ответ

0 голосов
/ 07 июня 2018

Поведение, описанное спрашивающим, невозможно или

, когда деструктор скомпилирован как комментарий:

означает, что деструктор закомментирован, и это вызываетпротечь.Это было подтверждено в комментарии.Без деструктора последнее распределение никогда не будет отложено.

Настоящая проблема Аскера - это Правило Трех Нарушений.

Краткая версия выглядит примерно так:

Есликласс требует определенного пользователем конструктора копирования, оператора присваивания или деструктора, он почти всегда требует всех трех.

Подробнее об этом здесь: Что такое правило трех?

В этом случае

void printIntArray(DynamicArray<int> arr)

принимает arr по значению.Это означает, что arr является копией переданного значения. В отсутствие пользовательского конструктора копирования arr внутри printIntArray будет указывать на то же распределение, что и источник.Копия будет уничтожена в конце printIntArray, и деструктор удалит выделение, оставив источник, указывающий на недопустимую память, так что ...

DynamicArray<int> arr(5); // allocated storage inside arr
printIntArray(arr); // made a copy of arr that points to same storage. 
                    // Storage freed when copy destroyed

arr.insert(1); // invokes undefined behaviour writing into invalid memory
arr.insert(2); // same
arr.insert(3); // same
printIntArray(arr); // copies arr again. This copy is pointing to the same invalid memory
                    // and will try to free the allocation a second time when the 
                    // copy is destroyed

Программа почти наверняка потерпит крах, вызываяневозможно вытечь из кода пользователя.Но поскольку программа потерпела крах, Crom знает только то, какие закулисные структуры, которые использовала среда выполнения, не были правильно убраны.

Что бы ни случилось, по состоянию на

arr.insert(1);

программа была глубоко погружена в Неопределенное поведение и все, что происходит после этого, - чье-то предположение.Утечки памяти - это наименьшее количество ваших забот.

Исправление: удовлетворение правилу трех

Реализация конструктора копирования и оператора присваивания ( Мы пока проигнорируем правило пяти). )

Вы можете изменить

void printIntArray(DynamicArray<int> arr)

на передачу по ссылке

void printIntArray(DynamicArray<int> & arr)

, а затем

DynamicArray(const DynamicArray &) = delete;
DynamicArray& operator=(const DynamicArray &) = delete;

и отключить всекопирование, но это кажется немного драконовским.

Вместо позволяет использовать Copy и Swap , потому что это просто и легко понять.Обратите внимание, что это также может быть неэффективно, поэтому это не всегда правильный инструмент.Здесь это соответствует тому, что мы хотим.

DynamicArray(const DynamicArray & src)  :
        size(src.size), numOfElements(src.numOfElements), arr(new T[size])
{
    for (int i = 0; i < numOfElements; i++)
    {
        arr[i] = src.arr[i];
    }
}


DynamicArray& operator=(const DynamicArray src)
{
    std::swap(size, src.size);
    std::swap(numOfElements, src.numOfElements);
    std::swap(arr, src.arr);
    return *this;
}

И поскольку тем, кто хочет копировать, им не нужно, мы все равно будем

void printIntArray(DynamicArray<int> & arr)

, как только мы закончим тестирование этогоконструктор копирования работает правильно.

...