这会导致内存泄漏吗?

时间:2011-06-07 20:44:16

标签: c++ memory pointers memory-leaks allocation

好吧,所以我和其他人有些分歧,我希望有人比我们中的任何一个人都更了解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 [] []数组中。我是对的,还是我弄错了?另一个人争辩说这会导致内存泄漏。

9 个答案:

答案 0 :(得分:8)

让我们看看每段代码。

1)分配内存

Tile* tile = new Tile();

这会在堆上创建一个新的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;

OR

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。由于map数组不是函数的本地数组,因此最好将其所有指针值设置为NULL,以便知道它何时包含有效指针以及何时不包含有效指针。

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

希望这会有所帮助。

答案 1 :(得分:4)

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

答案 2 :(得分:2)

如果您之后delete map addTile,则会发生泄漏的教科书示例。不,记忆当然不会被解除分配。为什么会这样?此外,您没有移动指针或任何内容,在调用tile后,{{1}}指针仍然有效。

答案 3 :(得分:2)

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

答案 4 :(得分:1)

  

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

很抱歉,但你的每一点都错了。

  • “指向[by] tile的内存已被解除分配。

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

  • “指针不再指向不存在的对象”

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

  • “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;
}

答案 5 :(得分:1)

我绝不是C ++内存管理方面的专家,但我会说你正在做的事情不会导致内存泄漏(假设你以后在某个地方正确删除了内存)。我对此的论证是你仍然有一个指向你分配的内存的有效指针,因此你仍然可以使用'delete'关键字删除。

将指针视为内存地址。使用“new”分配内存时,应确保将该地址存储在某处,以便实际访问数据。地址存储在/作为指针。因此,如果丢失指针(通过超出范围,如在代码块的示例中),则无法释放已分配的内存,因为您无法再知道数据在内存中的位置。但是,在您的示例中,您仍然在“map”数组中保留一个指针,因此您仍然可以释放已分配的内存。

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

答案 6 :(得分:0)

这会在堆上分配对象:

Tile* tile = new Tile();

这没有任何作用:

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

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

答案 7 :(得分:0)

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

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

这样你就不会泄漏记忆。

答案 8 :(得分:0)

您发布的代码中没有重新分配。您分配了一个新的Tile对象,addTile()实现将获得它的所有权。

本身没有内存泄漏;它取决于Level类的实现,它持有分配的Tile对象。如果在某个时刻它delete是来自内部地图的tile对象,那么就没有内存泄漏,否则就会有。