好吧,所以我和其他人有些分歧,我希望有人比我们中的任何一个人都更了解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 [] []数组中。我是对的,还是我弄错了?另一个人争辩说这会导致内存泄漏。
答案 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对象。你不是在任何地方复制任何值 - 它们都是指针,它们都是引用。也没有明显的理由让你进行动态分配,如果你说map
是Tile[][]
,那么我甚至惊讶于这甚至可以编译。
答案 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 [] []数组中。
很抱歉,但你的每一点都错了。
一般规则是这样的:每个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;
}
答案 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对象,那么就没有内存泄漏,否则就会有。