简体   繁体   English

unique_ptr的向量是什么?

[英]vector of unique_ptr deleting?

I have an issue with a segfault that I can not figure out. 我有一个我无法弄清楚的段错误问题。 It's from an EntityManager for a small game engine I'm working on. 它来自一个EntityManager用于我正在研究的小型游戏引擎。 I can add the Ship Entity , and the Ship can add 1 Bullet Entity , but it segfaults if I try to add more than 1 Bullet . 我可以添加Ship Entity ,并且Ship可以添加1个Bullet Entity ,但如果我尝试添加超过1个Bullet则会发生段错误。 I've been trying to figgure this out for the past day now. 我一直试图在过去的一天里把这个想象成现实。 Below is a small excerpt from the actual code. 以下是实际代码的一小段摘录。

#include <vector>
#include <memory>

struct EntityManager;
struct Entity {
    Entity(EntityManager* manager) : manager(manager) { }
    virtual ~Entity() { }
    virtual void update() = 0;

    EntityManager* manager;
};
struct EntityManager {
    void update() {
        for (auto& entity : entities) {
            entity->update();
        }
    }
    void add(Entity* e) {
        entities.emplace_back(e);
    }
    std::vector<std::unique_ptr<Entity>> entities;
};
struct Bullet : public Entity {
    Bullet(EntityManager* manager) : Entity(manager) { printf("Bullet ctor\n"); }

    virtual void update() override { }
};
struct Ship : public Entity {
    Ship(EntityManager* manager) : Entity(manager) { }

    virtual void update() override {
        printf("Adding Bullet\n");
        manager->add(new Bullet(manager));
    }
};
int main() {
    EntityManager manager;
    manager.add(new Ship(&manager));

    int loops{0};
    while (loops < 100) {
        manager.update();
        loops++;
        printf("Completed Loop #%d\n", loops);
    }
    return 0;
}

In the actual code, everything is in their own .h/.cpp files, and classes instead of structs, but the issue is the same. 在实际的代码中,一切都在他们自己的.h / .cpp文件中,而是在类而不是结构中,但问题是相同的。 The output is `Adding Bullet // Bullet ctor // Completed Loop #1 // Adding Bullet // Bullet ctor // Signal: SIGSEGV (Segmentation fault) 输出是`Adding Bullet // Bullet ctor // Completed Loop#1 //添加Bullet // Bullet ctor //信号:SIGSEGV(分段错误)

The segfault happens in the EntityManager::update() on the entity->update(); segfault发生在entity->update();EntityManager::update() entity->update(); line. 线。

The problem is that this loop modifies the vector: 问题是这个循环修改了向量:

    for (auto& entity : entities) {
        entity->update();
    }

You are busy iterating through it when you modify the vector to add a new element, which invalidates the iterators being used to traverse the container. 当您修改向量以添加新元素时,您正在忙于迭代它,这会使用于遍历容器的迭代器无效。

The range-based for loop is expanded by the compiler to: 基于范围的for循环由编译器扩展为:

auto begin = entities.begin(), end = entities.end();
for (; begin != end; ++begin)
  begin->update();

The call to begin->update() adds a new element to the vector, which invalidates all iterators into the container, so the ++begin is undefined behaviour. begin->update()的调用会向向量添加一个新元素,这会使所有迭代器无效进入容器,因此++begin是未定义的行为。 In practical terms, begin no longer points into the vector (because it has reallocated and freed the old memory that begin pointed to) so the next begin->update() call dereferences an invalid iterator, accessing freed memory and seg-faulting. 实际上, begin不再指向向量(因为它已经重新分配并释放了begin指向的旧内存),因此下一个begin->update()调用取消引用无效迭代器,访问释放的内存和seg-faulting。

To do it safely you probably want to use indices not iterators: 为了安全地做到这一点,你可能想要使用索引而不是迭代器:

for (size_t i = 0, size = entities.size(); i != size; ++i)
  entities[i].update();

This captures the size at the start of the loop and so only iterates up to the last element that exists when the loop starts, so new elements added to the end will not get visited. 这将捕获循环开始时的大小,因此只迭代循环开始时存在的最后一个元素,因此添加到末尾的新元素将不会被访问。

This still works when the vector is modified because you don't store iterators or pointers to elements, only an index. 当修改向量时,这仍然有效,因为您不存储迭代器或指向元素的指针,只存储索引。 As long as you don't remove elements from the vector the index is still valid even after inserting new elements. 只要不从向量中删除元素,即使插入新元素,索引仍然有效。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM