【问题标题】:Deleting pointers in destructor在析构函数中删除指针
【发布时间】:2014-12-30 05:35:06
【问题描述】:

我在类的构造函数中分配了一些指针,然后尝试在其析构函数中删除:

TileMap::TileMap(int x, int y) {

    mapSize.x = x;
    mapSize.y = y;

    p_p_map = new Tile*[x];

    for(int i = 0; i < x; i++) {

        p_p_map[i] = new Tile[y];

    }

    randomize();

}

TileMap::~TileMap() {

    for(int i = 0; i < mapSize.x; i++) {

        delete p_p_map[i];

    }

    delete p_p_map;

}

void TileMap::randomize() {

    for(int i = 0; i < mapSize.x; i++) {

        for(int j = 0; j < mapSize.y; j++) {

            p_p_map[i][j] = *new Tile(Tile::TileSize * i, Tile::TileSize * j, TileType::randomType());

        }

    }

}

在程序结束时调用析构函数来释放我分配的指针的内存,但是当它到达“删除 p_p_map[i];”时在析构函数中,XCode 通知我指针未分配。我是 C++ 新手,但我觉得我非常明确地将内存分配给 randomize() 函数中的指针。

我犯了什么错误?

【问题讨论】:

  • 您将从使用 std::vectorstd::unique_ptr / boost::unique_ptrstd::shared_ptr / boost::shared_ptr 而不是自己管理内存中受益匪浅。
  • 听起来您没有遵循Rule of Three,如果您坚持通过自虐式处理原始指针来管理动态对象,则很容易破坏。使用std::vector

标签: c++ xcode macos pointers


【解决方案1】:

您必须将deletenew 匹配,并将delete[]new[] 匹配。将一个与另一个混合会导致问题。所以如果你这样做:

p_p_map = new Tile*[x];

你必须像这样删除它:

delete[] p_p_map;

一样
delete[] p_p_map[i];

如果你创建类似的东西:

pSomething = new Type;

然后你像这样删除它:

delete pSomething;

【讨论】:

  • OP 的样本也会在 randomize() 函数中产生泄漏。
  • 确实,*new 几乎总是内存泄漏的标志,通常应该完全省略。这里也;只使用... = Tile( i, j, type)
【解决方案2】:

我犯了什么错误?

一些:

首先,正如@uesp 指出的那样,您不匹配新调用和删除调用

第二,你使用的是“内存泄漏算子”:

p_p_map[i][j] = *new Tile(Tile::TileSize * i, Tile::TileSize * j, TileType::randomType());

构造new Tile(...) 分配内存。然后,这个内存(没有存储在任何地方)被取消引用,并将结果分配给 p_p_map[i][j]。

因为指针没有存储在任何地方,所以被泄露了。

第三,你不尊重 RAII。虽然这在技术上本身并不是一个错误,但您编写代码的方式是不安全的,并且在内存不足的情况下,您会得到 UB。

例如,如果您构造一个具有较大 x 和 y 值的 Tile 实例,会发生以下情况:

TileMap::TileMap(int x, int y) { // e.g. (x = 1024 * 1024, y = 1024 * 1024 * 1024)

    mapSize.x = x;
    mapSize.y = y;

    p_p_map = new Tile*[x]; // allocate 1049600 pointers block

    for(int i = 0; i < x; i++) {

        p_p_map[i] = new Tile[y]; // run out of memory (for example) half way through the loop

    }

    randomize();
}

根据分配失败的位置,您的构造函数将无法完成执行,这意味着您的 TileMap 实例是“半构造”(即处于无效状态)并且不会调用析构函数。

在这种情况下,类分配的所有内容都会泄漏,并且(尤其是如果您分配了较大的大小)您的应用程序会处于内存不足的状态。

要解决此问题,请确保每个指针由类的不同实例管理(RAII 的一部分)。这确保了如果分配失败,分配的资源在退出范围之前被释放,作为堆栈展开的一部分(正如@CaptainObvlious 所说,对数组使用 std::vector 对每个元素使用 std::unique_ptr )。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-03-13
    • 2013-10-01
    • 2021-04-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-01-03
    • 2013-10-10
    相关资源
    最近更新 更多