如果已删除,如何从向量中删除对象

Posted

技术标签:

【中文标题】如果已删除,如何从向量中删除对象【英文标题】:How to remove object from a vector if it was deleted 【发布时间】:2021-04-09 11:55:16 【问题描述】:

我正在制作一个玩家可以射击的主机游戏。玩家按下空格键后,会创建一个新对象并将其放置在地图上,并且地图包含指向该对象的指针。这些对象存储在向量中。我读了一篇关于向量的文章,上面写着:

向量与动态数组相同,可以在插入或删除元素时自动调整大小

我以为我删除一个物体后,矢量会自动缩小,所以每当其中一个弹丸撞到墙上时我就这样做了:

delete entities[x + y * width];
entities[x + y * width] = nullptr;

但是向量的大小没有改变。我需要从向量中手动删除对象吗?我有一个想法,为每个存储其状态(死/活)的弹丸创建一个布尔变量,以便在循环的帮助下手动将其从向量中删除。有更好的方法吗?

另外,将射弹存储在矢量中是否安全?我读到该向量将在达到最大大小时将自身复制到内存的另一段。之后我的地图中的指针会发生什么?

代码如下所示:

#include <vector>
#include <iostream>
#include <conio.h>
class Entity 
public:
    int x, y;
    int getX()  return x; 
    int getY()  return y; 
;

class Projectile : public Entity
public:
    Projectile(int x, int y) 
        this->x = x;
        this->y = y;
    
;
class Map 
public:
    Entity** entities;
    int width, height;
    Map(int width, int height) 
        this->width = width;
        this->height = height;
    
    void spawn(Entity* entity) 
        entities[entity->getX() + entity->getY() * width] = entity;
    
;

int main() 
    Map map(10, 10);
    std::vector<Projectile*> projectiles;

    map.entities = new Entity* [map.width * map.height];
    int x, y;

    while (true) 
        if (_getch() == 32) 
            std::cin >> x >> y;
            projectiles.push_back(new Projectile(x, y));
            map.spawn(*projectiles.begin());
         

        //...
        bool hitWall = true;

        if (hitWall) 
            delete map.entities[x + y * map.width];
            map.entities[x + y * map.width] = nullptr;
        
    

    return 0;

【问题讨论】:

在向量中存储指向射弹的原始拥有指针可能不安全。如果您要存储对象,您只需调用entities.erase(...)。你能显示minimal reproducible example吗? @largest_prime_is_463035818 我添加了代码。 在您的代码中 entities 不是向量。如果你写了std::vector&lt;Entity*&gt; entities;(存储指针的警告适用,请参阅 cmets 和答案) @spectras 游戏中的所有实体(敌人/玩家等)都存储在数组entities 中,但射弹在向量中:std::vector&lt;Projectile*&gt; projectiles; @kastrycznik 仅供参考——Entity 缺少虚拟析构函数,因此您现在通过基类指针的实体 delete 是未定义的行为。第二件事是,除非您是一名高级 C++ 程序员,知道他们需要了解的有关指针和动态内存管理的所有知识,并且有充分的理由编写此类代码,否则Entity** entities; 是一种代码味道。 【参考方案1】:

根据您的示例代码,我假设您的向量定义如下:

std::vector<YourType*> entities;

因此,您的向量不包含YourType 对象,而是pointer to YourType。也就是说,向量管理的元素是指针,而不是指向的对象。

因此,当您这样做 delete entities[x + y * width]; 时,您确实删除了 YourType 实例,但指针仍然存在并且它仍然存在于您的向量中。

如果您将该语句分解为等效的 2 行,可能会更容易可视化:

YourType * pointer = entities[x + y * width];
delete pointer;

要真正从向量中移除指针,你需要这样说:

entities.erase(entities.begin() + x + y * width);

这将从数组中删除指针(也将所有内容移出该索引)。您仍然需要自己执行delete,因为向量只管理指针,而不是YourType


请注意,除非您有充分的理由,否则您可能不应将指针存储在向量中,而应将对象本身存储。这将消除您的困惑:

std::vector<YourType> entities;

// deleting a YourType:
entities.erase(entities.begin() + x + y * width);

不再有deletenew,因为对象直接在向量中。所以向量会为你管理它,而不是仅仅管理指针并让你处理指向的对象。


问题编辑后更新:

在您的情况下,您确实有充分的理由,因为您实际上存储了一个非拥有指针。因此entitiesprojectiles 存储指针可能是有意义的,因此它们实际上指向相同的对象。

所以两个向量都会管理它们的指针,但是你必须考虑这两个指针(一个来自实体,一个来自射弹)的生命周期如何与对象本身交互。

删除对象不会删除指针,在两个数组中都没有。

【讨论】:

【参考方案2】:

您需要使用迭代器。像这样:

entities.erase(entities.begin() + x + y * width)

关于安全性,对于经常实例化和销毁的小对象,最好直接存储对象而不是指针。这消除了堆分配带来的内存泄漏或悬空指针的可能性。

【讨论】:

以上是关于如果已删除,如何从向量中删除对象的主要内容,如果未能解决你的问题,请参考以下文章

从向量中删除对象会破坏 C++ 中另一个对象中的对象

如何从指向对象的指针向量中删除对象? [复制]

从向量中删除项目

对复制构造函数已被删除的对象向量进行迭代 (C2280)

从向量中删除opencv rect对象

如何使用类成员搜索对象指针向量?