Это вызвало бы утечку памяти? - PullRequest
0 голосов
/ 08 июня 2011

Хорошо, у меня возникли некоторые разногласия с кем-то еще, и я надеялся, что кто-то, кто знает больше о c ++, чем кто-либо из нас, сможет это выяснить. Скажем, у нас есть этот блок кода где-то внутри функции (для движка тайлакарты):

void loadTiles()
{
Tile* tile = new Tile();
Level->addTile(x, y, tile); //x and y are just arbitrary ints.
/* when addTile is called, it fills the values of the chunk of memory pointed to by tile to the predefined chunk of memory created in the Level object. */
//Then, to remove the dangling pointer safely,
tile = NULL;
} //Then the actual memory pointed to by tile is deallocated here.

Класс Level имеет двумерный массив Tiles, называемый map [] [], и его функция addTile выглядит точно так же:

void Level::addTile(int x, int y, Tile *tile) 
{
    map[x][y] = tile;
}

Память, указанная для плитки, была освобождена, указатель больше не указывает на несуществующий объект, и значения для объекта плитки были по существу скопированы в массив map объекта [] [] объекта. Я прав или я ошибаюсь? Другой человек утверждает, что это приведет к утечке памяти.

Ответы [ 9 ]

8 голосов
/ 08 июня 2011

Давайте рассмотрим каждый фрагмент кода.

1) Выделение памяти

Tile* tile = new Tile();

Это создает новый объект Tile в куче и сохраняет адрес памятив переменной плитки.Имейте в виду, что переменная плитка - это только указатель, а не сам объект.

2) Копирование ссылки

void Level::addTile(int x, int y, Tile *tile) { map[x][y] = tile;}

Приведенная выше функция просто берет указательи сохраняет его в многомерный массив для будущего использования.В контексте всего перечисленного кода теперь будет две ссылки на объект ... плитку * в исходной вызывающей функции и запись в многомерном массиве.Опять же, имейте в виду, что это указатели (всего 4 байта, в зависимости от архитектуры вашей системы).

3) Установка указателя на NULL

tile = NULL;

Результатом этого кода является то, что плитка переменной указателя больше не будет указывать на созданный объект в куче.Однако объект все еще будет существовать.На данный момент, в контексте всего кода, у вас все еще будет указатель на объект из-за массива map [] [].

В действительности вам не нужна эта строка кода.плитка не является висящим указателем, так как она указывает на действительный объект (до того, как вы установите его в NULL).Код также не разрушает объект.Поскольку плитка является локальной переменной, она будет очищена при выходе из области действия функции.Будет очищен только указатель, а не объект, на который он указывает.Установка этого значения в NULL в этом сценарии очень мало, кроме циклов траты.

Это также не утечка памяти, скажем так.У вас все еще есть действительный указатель на объект в массиве map [] [], поэтому вы всегда можете использовать эту ссылку для очистки памяти.

4) Что нужно сделать

Вам необходимо удалить объект где-нибудь.В C ++ это ключевое слово delete.

delete tile;

ИЛИ

delete map[x][y];

Теперь имейте в виду, что после запуска приведенного выше кода память в куче будет освобождена обратно воперационная система и ваше приложение больше не могут безопасно обращаться к памяти.Следовательно, любой вызов map [x] [y] -> {SomeMethod} приведет к исключению нарушения прав доступа.Указатель, хранящийся в карте [x] [y], теперь является висящим указателем (он указывает на адрес памяти, недопустимый для этого типа).

Если вам нужно удалить плитку из карты уровня передвы уничтожаете уровень, вы делаете это:

void Level::deleteTile(int x, int y) 
{ 
    if (map[x][y] != NULL)
    {
        delete map[x][y];
        map[x][y] = NULL;
     }
}

Я бы также изменил метод addTile на что-то вроде этого:

void Level::addTile(int x, int y, Tile *tile)
{
    deleteTile(x, y);
    map[x][y] = tile;
}

Наконец, когда вы уничтожаете объект уровня,вам нужно сделать что-то вроде этого:

void ~Level()
{
    for (int i; i<MaxX; i++)
    {
        for (int j; j<MaxY; j++)
        {
            delete map[i][j];
        }
    }
}

Когда вы создаете объект уровня, вы должны обнулить массив map [] [], чтобы все значения также были равны NULL.Поскольку массив карт не является локальным для функции, рекомендуется установить для всех его значений указателя значение NULL, чтобы вы знали, когда он содержит действительный указатель, а когда - нет.

Если выдинамически создавайте (используя новое ключевое слово) массив map [] [], вам также нужно будет очистить его память, используя ключевое слово delete.

Надеюсь, это поможет.

4 голосов
/ 08 июня 2011

Это приведет к утечке памяти.В размещенной области видимости нет абсолютно никакого кода, который бы разрушал объект Tile, который вы динамически выделяли.Вы никуда не копируете значения - все они указатели, которые являются ссылками.Нет никакой очевидной причины, по которой вы сделали динамическое распределение, и если, как вы говорите, map - это Tile[][], то я удивлен, что это даже компилируется.

2 голосов
/ 08 июня 2011

Нет, это в основном переводит владение с tile на map[x][y] в объекте Level. Вы должны убедиться, что указанный объект освобожден позже (т. Е. В деструкторе Level. Вы также должны изменить свой метод addTile(), чтобы убедиться, что map[x][y] ни на что не указывает, иначе память, на которую он ранее указывал будет течь.

2 голосов
/ 08 июня 2011

Пример утечки из учебника, если вы не delete это из map позже.И нет, память, конечно, не освобождается.С чего бы это?Кроме того, вы не перемещаете указатель или что-либо еще, после вызова addTile указатель tile все еще действует.

1 голос
/ 08 июня 2011

Я ни в коем случае не эксперт по управлению памятью в C ++, но я бы сказал, что то, что вы делаете, не приведет к утечкам памяти (учитывая, что вы где-то правильно удаляете память позже).Моя аргументация заключается в том, что у вас все еще есть действительный указатель на выделенную память, поэтому вы все равно можете удалить, используя ключевое слово «delete».

Думайте об указателях как об адресах памяти.Когда вы выделяете память с помощью «new», вы должны убедиться, что вы храните этот адрес где-то, чтобы вы могли получить доступ к данным.Адрес хранится в / как указатель.Поэтому, если вы потеряете указатель (выйдя из области видимости, как в вашем примере с блоком кода), вы потеряете возможность освободить выделенную память, потому что вы больше не можете знать, где находятся данные в памяти.Однако в вашем примере вы по-прежнему сохраняете указатель в массиве «map», поэтому вы все равно должны иметь возможность освободить выделенную память.

Главное, что здесь следует помнить, это то, что указатель НЕ являетсясами данные.Потеря указателя - это неплохо, если у вас есть еще один указатель на данные.Как только вы потеряете ВСЕ указатели на данные, вы отправитесь в область утечки памяти.

1 голос
/ 08 июня 2011

Память, указанная для плитки, была освобождена, указатель больше не указывает на несуществующий объект, и значения для объекта плитки были по существу скопированы в массив map объекта [] [].

Извините, но вы ошибаетесь в каждом из ваших пунктов.

  • "Память, указанная плиткой [by], была освобождена.

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

  • «Указатель больше не указывает на несуществующий объект»

Переменная с именем tile во фрагменте анонимного кода указывает на выделенную память до тех пор, пока ей не будет присвоено NULL. Переменная с именем tile в Level::addTile указывает на выделенную память в течение всего времени ее жизни.

  • "значения объекта мозаики были по существу скопированы в массив карты уровня [] []"

Значение указателя, а не значения объекта было скопировано.

Следующий фрагмент кода делает то, что, как вы думаете, сделал ваш код:

{
  Tile tile;
  Level->addTile(x, y, tile); //x and y are just arbitrary ints.
  /* when addTile is called, it fills the values of the chunk of memory pointed to by tile to the predefined chunk of memory created in the Level object. */
} //Then the actual memory pointed to by tile is deallocated here.

void Level::addTile(int x, int y, Tile &tile) 
{
    map[x][y] = tile;
}
0 голосов
/ 08 июня 2011

В размещенном вами коде не происходит освобождения. Вы выделяете новый объект Tile, и реализация addTile () становится его владельцем.

Нет утечки памяти как таковой; это зависит от реализации класса Level, который содержит выделенный объект Tile. Если в какой-то момент это delete s мозаичных объектов из внутренней карты, то утечки памяти нет, в противном случае, есть.

0 голосов
/ 08 июня 2011

Сделать map 2D-массив указателей на Tile.

Затем, когда вы будете готовы освободить ресурсы, delete все элементы в map и затем удалите саму карту (если она выделена с помощью new).

Таким образом, вы не потеряете память.

0 голосов
/ 08 июня 2011

Это выделяет объект в куче:

Tile* tile = new Tile();

Это ничего не делает:

tile = NULL;
} //Then the actual memory pointed to by tile is deallocated here.

Поскольку объект не удален, это утечка памяти.

...