插入无序地图时读取访问冲突

Read access violation when inserting into unordered map

我在搞乱 a small ECS implementation found here。我慢慢地复制代码来理解它并且它工作正常。但是,在我尝试添加 ECS 的“系统”部分之前,我尝试将功能分成不同的 classes 并使用“协调器”class 抽象所有内容,而不是一个大的命名空间.

所以我有这样的东西(对不起,如果我的代码提前不好,还在学习C++):

// ECSCoordinator.h

#ifndef I3D_ECS_COORDINATOR_H
#define I3D_ECS_COORDINATOR_H

#include <memory>
#include "Entity.h"
#include "EntityManager.h"

#include "ComponentContainer.h"

class ECSCoordinator {
public:
    ECSCoordinator();

    Entity create_entity();
    void delete_entity();

    template<typename Component>
    void add_component_to(Entity e, Component c);

private:
    template<class Component> 
    static ComponentContainer<Component> registry;
    std::unique_ptr<EntityManager> entity_manager;
};

template<typename Component>
void ECSCoordinator::add_component_to(Entity e, Component c) {
    registry<Component>.insert(e, c);
}

// ECSCoordinator.cpp

#include "ECSCoordinator.h"
#include "ECS/Entity.h"
#include "ECS/EntityManager.h"

ECSCoordinator::ECSCoordinator()
    : entity_manager(std::make_unique<EntityManager>()) {}

Entity ECSCoordinator::create_entity() {
    return entity_manager->create_entity();
}

#endif // I3D_ECS_COORDINATOR_H

我必须决定的一件事是在哪里声明 registry。在最初的实现中,它是在命名空间的范围内声明的,但是一旦我将内容移入 classes 中,我就不希望有一个全局变量就在那里。因此,我将变量模板设为协调器的静态 class 成员。我相信这些是等价的? ComponentContainer 的前向声明不再起作用,因为我在 header 中定义了 add_component_to,所以我不得不改为 #include "ComponentContainer.h",这意味着我必须移动静态定义在 ComponentContainer 到他们自己的 .cpp 文件中(如果重要的话)。

除此之外,让 Entity.h 成为 POD(仅包含一个无符号整数)并 EntityManager.h 负责创建具有递增 ID 的实体,我没有做太多更改。

所以当我想使用系统时,我想改为这样做:

std::unique_ptr<ECSCoordinator> coordinator = std::make_unique<ECSCoordinator>();
Entity fish = coordinator->create_entity();
coordinator->add_component_to(fish, Animal("Fish"));

但是,当我尝试将(键,值)添加到无序映射(tiny_ecs.hpp 的第 100 行)时,这些更改会导致读取访问冲突:

map_entity_component_index[e.id] = component_index;

我可以肯定地确认 e.id = 0component_index = 1 是第一次添加。我可以 运行 无序地图上的一个方法,比如 size() 就在这个错误之前就好了。但是,尝试向地图添加内容会导致崩溃。

我不确定如何解释我在调试器中看到的错误:

感谢所有提供帮助的人。

答案最终是由于静态订单初始化失败。解决方案是通过 getter 访问协调器中的静态模板变量以确保其初始化:

class ECSCoordinator {
    //....

    template<class Component>
    ComponentContainer<Component>& get_registry();

    //...
};

template<typename Component>
void ECSCoordinator::add_component_to(Entity e, Component c) {
    get_registry<Component>().insert(e, c);
}

template<class Component>
ComponentContainer<Component>& ECSCoordinator::get_registry() {
    static ComponentContainer<Component> registry;
    return registry;
}