这会导致内存泄漏吗

Would this cause a memory leak?

本文关键字:泄漏 内存      更新时间:2023-10-16

好吧,我和其他人有点意见分歧,我希望比我们任何一个人都更了解c++的人能澄清这一点。假设我们在一个函数中的某个位置有一段代码(用于tilemap引擎):

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类有一个名为map[][]的2D Tile数组,它的addTile函数看起来就像这样:

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

指向tile的内存被释放,指针不再指向不存在的对象,tile对象的值基本上被复制到Level对象的map[][]数组中。我是对的,还是错了?另一个人认为这会导致内存泄漏。

让我们看看每一段代码。

1)分配内存

Tile* tile = new Tile();

这将在堆上创建一个新的Tile对象,并将内存地址存储在变量Tile中。请记住,变量瓦片只是一个指针,而不是对象本身。

2)复制参考

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

上面的函数只需获取一个指针并将其保存到多维数组中以备将来使用。在列出的所有代码的上下文中,现在将有两个对对象的引用。。。原始调用函数中的tile*和多维数组中的条目。同样,请记住,这些都是指针(只有4个字节,具体取决于您的系统体系结构)。

3)将指针设置为NULL

tile = NULL;

此代码的结果是指针变量tile将不再指向堆上创建的对象。不过,该对象仍然存在。此时,在所有代码的上下文中,由于map[][]数组,您仍然有一个指向对象的指针。

实际上,您不需要这行代码。tile不是一个悬空指针,因为它指向一个有效的对象(在您将其设置为NULL之前)。该代码也不会破坏对象。由于tile是一个局部变量,当它的函数范围退出时,它将被清除。只有指针会被清除,而不是它所指向的对象。在这种情况下,将其设置为NULL除了浪费周期外,几乎没有什么作用。

这也不是内存泄漏。您仍然有一个指向map[][]数组中对象的有效指针,因此您可以始终使用该引用来清理内存。

4)您需要做什么

你需要在某个地方删除该对象。在C++中,这是delete关键字。

delete tile;

delete map[x][y];

现在,请记住,一旦上面的代码运行,堆上的内存将被释放回操作系统,您的应用程序将无法再安全地访问内存。因此,任何对map[x][y]->{SomeMethod}的调用都会导致访问冲突异常。存储在map[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;
}

最后,当您对Level对象进行去库存时,您需要执行以下操作:

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

创建Level对象时,应该将map[][]数组清零,这样所有值也都将为NULL。由于映射数组不是函数的本地数组,因此最好将其所有指针值设置为NULL,以便您知道它何时包含有效指针,何时不包含。

如果动态创建(使用new关键字)map[][]数组,则还需要使用delete关键字清理其内存。

希望这能有所帮助。

这将泄漏内存。发布的范围中绝对没有任何代码会破坏动态分配的Tile对象。你不会在任何地方复制任何值——它们都是指针,都是引用。您也没有明显的理由进行动态分配,如果正如您所说,mapTile[][],那么我很惊讶它甚至可以编译。

如果以后不从mapdelete,则是泄漏的教科书示例。不,内存当然不会被释放。为什么会这样?此外,您没有移动指针或任何东西,在调用addTile之后,tile指针仍然有效。

否,这基本上将对象Level中的所有权从tile转移到map[x][y]。您应该确保指向的对象稍后被释放(即在Level的析构函数中)。您还应该更改addTile()方法以检查map[x][y]是否指向任何东西,否则它之前指向的内存将泄漏。

指向tile的内存被释放,指针不再指向不存在的对象,tile对象的值基本上被复制到Level对象的map[][]数组中。

对不起,你的每一点都错了。

  • "[由]磁贴指向的内存已解除分配

一般规则是:每个new必须恰好有一个匹配的delete。由于您使用new分配内存,并且从未调用过delete,因此永远不会释放内存。

  • "指针不再指向不存在的对象"

匿名代码段中名为tile的变量指向已分配的内存,直到NULL被分配给它。Level::addTile中名为tile的变量在其整个生命周期中都指向已分配内存。

  • "tile对象的值基本上被复制到Level对象的map[][]数组中"

复制的是指针的值,而不是对象的值。

下面的代码片段做了您认为您的代码所做的事情:

{
  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;
}

我绝对不是C++内存管理方面的专家,但我想说,你所做的不会导致内存泄漏(假设你稍后确实正确地删除了内存)。我的论点是,你仍然有一个指向你分配的内存的有效指针,因此你仍然可以通过使用"delete"关键字来删除它。

把指针想象成内存地址。当您使用"new"分配内存时,您应该确保将该地址存储在某个地方,以便能够实际访问数据。地址作为指针存储在/中。因此,如果您丢失了指针(通过超出范围,如代码块的示例),您将失去释放已分配内存的能力,因为您无法再知道数据在内存中的位置。然而,在您的示例中,您仍然在"map"数组中保留一个指针,因此您应该仍然能够释放分配的内存。

这里要记住的关键是指针不是数据本身。只要你还有另一个指向数据的指针,丢失一个指针也不错。一旦你丢失了所有指向数据的指针,那么你就进入了内存泄漏的领域。

这会分配堆上的对象:

Tile* tile = new Tile();

这毫无作用:

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

由于该对象未被删除,因此它是内存泄漏。

使map成为指向Tile的指针的2D数组。

然后,当您准备释放资源时,delete map中的所有元素,然后删除映射本身(如果使用new分配)。

这样你就不会泄露内存。

您发布的代码中没有进行任何释放。您分配了一个新的Tile对象,addTile()实现获得了它的所有权

内存本身没有泄漏;它取决于Level类的实现,该类持有分配的Tile对象。如果在某个时刻它delete是来自内部映射的tile对象,则不存在内存泄漏,否则存在.