【问题标题】:Remove and delete pointers that match a condition in a vector删除和删除与向量中的条件匹配的指针
【发布时间】:2020-02-01 19:22:05
【问题描述】:

我有一个std::vector,我想从满足isDestroyed() 条件的向量中删除指针,但还要对指针调用delete。

我做了以下操作,但它需要在向量上循环两次。有没有更有效的方法?

std::vector<GameObject*> gameObjects;
std::vector<GameObject*> destroyedObjects;
// Get objects to be deleted
std::copy_if (gameObjects.begin(), gameObjects.end(), std::back_inserter(destroyedObjects), [](GameObject* b){return b->isDestroyed();} );
// Remove objects from vector
gameObjects.erase(
    std::remove_if(
            gameObjects.begin(),
            gameObjects.end(),
            [](GameObject* p) { return p->isDestroyed(); }
    ),
    gameObjects.end()
);
// Delete the objects
for (GameObject* o : destroyedObjects)
    delete o;

【问题讨论】:

  • 向相反方向循环,这样您就可以在不移动其他迭代器的情况下删除一个元素。

标签: c++ stl c++17


【解决方案1】:

std::unique_ptr免费删除:

std::vector<std::unique_ptr<GameObject>> gameObjects;

// Remove objects from vector
gameObjects.erase(
    std::remove_if(
            gameObjects.begin(),
            gameObjects.end(),
            [](const auto& p) { return p->isDestroyed(); }
    ),
    gameObjects.end()
);

我建议改用它。也避免了忘记删除或重复删除等错误。

【讨论】:

    【解决方案2】:

    这应该可行:

    std::vector<GameObject*> gameObjects;
    auto end = std::stable_partition(
                gameObjects.begin(),
                gameObjects.end(),
                [](GameObject* p) { !return p->isDestroyed(); }
        );
    for (auto i = end; i < gameObjects.end(); i++) {
        delete *i;
    }
    gameObjects.erase(end, gameObjects.end());
    

    【讨论】:

    • 这不能保证有效,因为remove_if 未指定范围的已删除部分中的值。它们不需要(根据我的经验也不会)与被删除的值一一对应。
    • @Kyle,你是对的(我从来没有使用过 remove_if 用于擦除以外的任何东西),stable_partition 应该可以完成这项工作
    【解决方案3】:

    您不需要 2 个向量。 std::remove_if()返回的迭代器可以用来知道哪些对象需要delete'd:

    std::vector<GameObject*> gameObjects;
    ...
    auto newEnd = std::remove_if(
        gameObjects.begin(), gameObjects.end(),
        [](GameObject* p) { return p->isDestroyed(); }
    );
    for(auto iter = newEnd; iter != gameObjects.end(); ++iter) {
        delete *iter;
    }
    gameObjects.erase(newEnd, gameObjects.end());
    

    如果您将矢量更改为保存std::unique_ptr&lt;GameObject&gt; 而不是GameObject*,则不再需要手动delete 对象:

    std::vector<std::unique_ptr<GameObject>> gameObjects;
    ...
    gameObjects.erase(
        std::remove_if(
            gameObjects.begin(), gameObjects.end(),
            [](std::unique_ptr<GameObject> &p) { return p->isDestroyed(); }
        ),
        gameObjects.end()
    );
    

    【讨论】:

    • @walnut 在这种情况下并不重要
    • 您不能使用remove_if返回的迭代器和结束之间的值,因为该范围内的值是未指定的,因此删除它们可能会导致UB。在这种情况下使用unique_ptr 很好,并且会做正确的事情。如果您想要一个可以在擦除之前安全删除已删除指针的实现,则需要改用std::partition
    猜你喜欢
    • 2015-08-09
    • 1970-01-01
    • 2012-06-01
    • 2013-09-24
    • 2012-08-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多