在自包含向量循环中删除时“向量迭代器不兼容”

Posted

技术标签:

【中文标题】在自包含向量循环中删除时“向量迭代器不兼容”【英文标题】:"Vector Iterators incompatible" when deleting in a self contained vector loop 【发布时间】:2016-07-21 06:25:58 【问题描述】:

我可以请求帮助以确认我的问题是否来自设计问题,或者是否有可能针对以下问题提供干净的解决方案:

实体.h

class CLEntity3D

public:
    CLEntity3D();
    virtual ~CLEntity3D();
    virtual void update() = 0;

    static std::vector<CLEntity3D*> vecEntity;  
;

Entity.cpp

int CLEntity3D::nbrEntity = 0;
std::vector<CLEntity3D*> CLEntity3D::vecEntity;

CLEntity3D::CLEntity3D()

    vecEntity.push_back(this);

CLEntity3D::~CLEntity3D()

    vecEntity.erase((std::remove(vecEntity.begin(), vecEntity.end(), this)), vecEntity.end());     

各种派生类正在通过程序创建/删除不同的实体对象,这一切都很好。

在一个场景类中,我有以下方法:

void CLScene::Update()  

    for (auto& iter : CLEntity3D::vecEntity) 
        iter->update();
    


void CLScene::ClearScene()

    for (auto& iter : CLEntity3D::vecEntity) 
        delete(iter); iter = nullptr;
    
    CLEntity3D::vecEntity.clear();

更新没问题,问题出在 ClearScene() 上。我得到一个“向量迭代器不兼容”的调试断言。

根据我的研究,常见问题似乎是因为迭代器来自不同的向量,我认为这不是问题所在。我认为问题在于调用 ClearScene() 时,每个 delete(iter) 通过 CLEntity3D 析构函数更改 vecEntity 的大小,因此使 ClearScene 循环中的迭代器无效。我说的对吗?

我的问题是: 有没有办法使用该设计从 CLScene 中删除所有 CLEntity3D 对象?

我想我可以让 CLScene 持有 vecEntity,这样可以消除问题,但这意味着 CLScene 必须管理所有实体的创建/删除,因此不能通用...

PS:我知道这个例子不是一个可以编译的例子,但是因为我的问题更多的是关于概念......请告知我是否应该提供其他方式。

【问题讨论】:

问题在于,在CLEntity3D 中,您将this 添加到vecEntity 向量中,但CLEntity3D 实例可能是动态构造的,也可能不是动态构造的(例如CLEntity3D entity; vs. CLEntity3D *entity = new CLEntity3D();)。如果没有进行动态分配,则不能使用delete 我猜你想写*iter = nullptr;而不是iter = nullptr;如果你改变迭代器它应该怎么去下一个? @SHR 这是一个for-range 循环,iter 变量实际上是指针本身,而不是迭代器。 @SHR 那里不是迭代器,他使用的是基于范围的 for 循环。 有充分的理由让每个CLScene 管理它自己的私有实体列表,而不是作为CLEntity3D 的静态成员并由CLEntity3D 管理。一方面,它将代码打开到多个场景的可能性。对于另一个vecEntity 是公开的。任何人都可以随时删除任何实体,让CLScene处于非常危险的境地。 【参考方案1】:

问题是,在基于范围的 for 循环内,您无法从基础向量中删除任何内容。

ClearScene 方法中的循环会删除 CLEntity3D 实例,它的析构函数会更改您在 for 循环中使用的同一向量。

一个相对简单的解决方法是将您的 ClearScene 更改为如下内容:

void CLScene::ClearScene()

    auto vectorCopy = CLEntity3D::vecEntity;
    for (auto& iter : vectorCopy) 
        delete iter;
    

这是可行的,因为循环对副本进行操作,而删除发生在原始副本上。

注意,循环后不需要清除原始向量,因为析构函数确保向量在删除每一项后为空。

或者按照评论的建议,您可以通过使用 while 循环来避免复制:

while (!CLEntity3D::vecEntity.empty()) 

   delete CLEntity3D::vecEntity.begin();

【讨论】:

检查 CLEntity3D 析构函数,它会从向量中删除当前项。 啊,我错过了。我认为代码太坏了,无法修复。 保存副本:while (! CLEntity3D::vecEntity.empty()) delete CLEntity3D::vecEntity.begin(); 这取决于修复的定义。复制向量显然解决了迭代器断言,但这里肯定存在更大的设计问题。

以上是关于在自包含向量循环中删除时“向量迭代器不兼容”的主要内容,如果未能解决你的问题,请参考以下文章

向量迭代器不兼容...但是为啥呢?

opencv“向量迭代器不兼容”

C++ for-each 语句触发“向量迭代器不兼容”断言失败:this->_Getcont() == 0

向量的C++参考

从向量中删除项目

C++ - 在自定义数据类型向量中按值匹配和替换元素