Является ли delete p, где p указатель на массив всегда утечка памяти? - PullRequest
7 голосов
/ 09 марта 2010

после обсуждения на совещании по программному обеспечению, которое я намеревался выяснить, приведет ли удаление динамически размещенного массива примитивов с простым delete к утечке памяти.

Я написал эту крошечную программу и скомпилировал ее с Visual Studio 2008, работающей на Windows XP:

#include "stdafx.h"
#include "Windows.h"

const unsigned long BLOCK_SIZE = 1024*100000;
int _tmain()
{
    for (unsigned int i =0; i < 1024*1000; i++)
    {
        int* p = new  int[1024*100000];
        for (int j =0;j<BLOCK_SIZE;j++) p[j]= j % 2;
        Sleep(1000);
        delete p;
    }
}

Я следил за потреблением памяти моего приложения с помощью диспетчера задач. Удивительно, но память была выделена и освобождена правильно, выделенная память не увеличивалась стабильно, как ожидалось

Я изменил свою тестовую программу для выделения массива не примитивного типа:

#include "stdafx.h"
#include "Windows.h"


struct aStruct
{
    aStruct() : i(1), j(0) {}

    int i;
    char j;
} NonePrimitive;

const unsigned long BLOCK_SIZE = 1024*100000;
int _tmain()
{
    for (unsigned int i =0; i < 1024*100000; i++)
    {
        aStruct* p = new  aStruct[1024*100000];
        Sleep(1000);
        delete p;

    }
}

после 10 минут работы не было значимого увеличения памяти

Я скомпилировал проект с уровнем предупреждения 4 и не получил предупреждений.

Возможно ли, чтобы среда Visual Studio отслеживала типы выделенных объектов, чтобы не было различий между delete и delete[] в этой среде?

Ответы [ 8 ]

19 голосов
/ 09 марта 2010

delete p, где p - массив, называется неопределенным поведением.

В частности, когда вы выделяете массив необработанных типов данных (целых), компилятору не нужно много работать, поэтому он превращает его в простой malloc (), поэтому delete p, вероятно, будет работать.

delete p завершится неудачно, как правило, когда:

  • p был сложным типом данных - удалить p; не узнает, как называть отдельных деструкторов.
  • пользовательский оператор перегружает оператор new [] и удаляет [], чтобы использовать кучу, отличную от обычной кучи.
  • оператор перегрузки отладочной среды new [] и delete [], чтобы добавить дополнительную информацию отслеживания для массива.
  • компилятор решает, что ему нужно хранить дополнительную информацию RTTI вместе с объектом, который удаляет p; не поймет, но удалит [] p; будет.
17 голосов
/ 09 марта 2010

Нет, это неопределенное поведение. Не делай этого - используй delete[].

В VC ++ 7–9 это работает , когда у рассматриваемого типа есть тривиальный деструктор , но он может перестать работать на более новых версиях - обычных вещах с неопределенным поведением. В любом случае, не делай этого.

3 голосов
/ 09 марта 2010

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

Более отвечающий стандартам ответ заключается в том, что это неопределенное поведение. Например, компилятор имеет полное право использовать другие пулы памяти для массивов, чем для элементов, не являющихся массивами. Выполнение одного способа, но удаление другого может привести к повреждению кучи.

Ваш компилятор может дать гарантии того, что стандарт этого не делает, но первая проблема остается. Для элементов POD, которые не имеют дополнительной памяти (или ресурсов, таких как файловые дескрипторы), вы можете быть в порядке.

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

3 голосов
/ 09 марта 2010

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

Я не думаю, что Visual Studio отслеживает, как вы распределили объекты, как массивы или простые объекты, и волшебным образом добавляет [] к вашему удалению. Вероятно, он компилирует delete p; в тот же код, как если бы вы выделяли p = new int, и, как я уже сказал, по какой-то причине это работает. Но ты не знаешь почему.

2 голосов
/ 09 марта 2010

Простое использование delete не вызовет деструкторы объектов в массиве. Хотя он будет возможно работать должным образом, он не определен, поскольку существуют некоторые различия в том, как именно они работают. Так что не стоит его использовать, даже для встроенных типов .

2 голосов
/ 09 марта 2010

нет, вы должны использовать delete[] при работе с массивами

1 голос
/ 09 марта 2010

Использование delete с [] говорит компилятору вызывать деструктор для каждого элемента массива. Если не использовать delete [], это может привести к утечкам памяти, если использовать массив объектов, которые используют динамическое распределение памяти, как показано ниже:

class AClass
{
public:
    AClass()
    {
        aString = new char[100];
    }
    ~AClass()
    {
        delete [] aString;
    }
private:
    const char *aString;
};

int main()
{
    AClass * p = new  AClass[1000];
    delete p; // wrong
    return 0;
}
1 голос
/ 09 марта 2010

Причина, по-видимому, заключается не в том, что утечка памяти связана с тем, что удаление, как правило, основано на свободе, которое уже знает, сколько памяти необходимо для освобождения. Тем не менее, часть c ++ вряд ли будет очищена правильно. Могу поспорить, что только деструктор первого объекта называется.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...