C++ 如何使用子类创建 unordered_map
C++ How to make an unordered_map with subclasses
我正在制作游戏引擎,目前正在研究实体组件系统。这里有一些代码可以更好地理解:
class Entity {
public:
...
void AddComponent(EntityComponent component, unsigned int id){
m_Components.insert({id, component});}
EntityComponent GetComponent(unsigned int id)
{
auto i = m_Components.find(id);
ASSERT(i != m_Components->end()); // ASSERT is a custom macro
return i->second;
}
private:
...
std::unordered_map<unsigned int, EntityComponent> m_Components;
};
也是一个简单的parent class EntityComponent,只处理ID(我跳过了一些代码,因为它在这个问题中无关紧要):
class EntityComponent {
public:
...
private:
unsigned int m_EntityID;
size_t m_PoolIndex;
};
它是 children。其中之一是 TransformComponent:
class TransformComponent : public EntityComponent {
public:
TransformComponent(unsigned int entityID, unsigned int poolID = 0, glm::vec2 position = glm::vec2(1.0,1.0), glm::vec2 scale = glm::vec2(1.0,1.0), float rotation = 0) : EntityComponent(entityID, poolID),m_Rotation(rotation), m_Scale(scale), m_Position(position){}
inline glm::vec2 GetPosition() { return m_Position; }
inline glm::vec2 GetScale() { return m_Scale; }
private:
glm::vec2 m_Position;
float m_Rotation;
glm::vec2 m_Scale;
};
所以问题是我想创建一个实体,向它添加一个 TransformComponent 并使用它的函数 GetPosition()、GetScale()。
创建实体并添加 TransformComponent 有效,但是当我想使用 GetComponent(id) 时,它 returns parent class EntityComponent, 所以这意味着我不能使用像 GetPosition() 等函数
如何更改代码,以便我可以将 EntityComponent 的不同 children 添加到 unordered_map 并使用它们的 ID 接收它们并使用它们public 方法?
如前所述,第一个问题是你应该存储EntityComponent*或EntityComponent的智能指针,以防止派生对象数据在分配给基时被擦除class。因此,容器类型将是以下之一:
std::unordered_map<int, EntityComponent*>
std::unordered_map<int, std::unique_ptr<EntityComponent>>
std::unordered_map<int, std::shared_ptr<EntityComponent>>
此外,应将虚拟析构函数添加到 EntityComponent 以正确删除派生的 classes:
class EntityComponent
{
virtual ~EntityComponent() = default;
// other stuff ...
};
何时可以使用 dynamic_cast 检查 EntityComponent 是否实际上是一个 TransformComponent:
EntityComponent* ec = GetComponentById(id); // suppose that function returns TransformComponent or EntityComponent
TransformComponent* tc = dynamic_cast<TransformComponent*>(ec);
if (tc != nullptr)
{
std::cout << "tc is transform component" << std::endl;
tc->GetPosition();
tc->GetScale();
}
但可能对于游戏引擎来说 dynamic_cast 在某些情况下可能会很慢。如果您知道从 EntityComponent 派生的所有 classes,您可以添加 Type 字段来检查实际对象类型,然后使用在编译时工作的 static_cast 转换为它:
enum class ComponentType
{
BASE,
TRANSFORM,
// other types
};
class EntityComponent
{
ComponentType _type;
public:
inline ComponentType GetType() const { return this->_type; }
inline EntityComponent(ComponentType type = ComponentType::BASE) : _type(type) { }
virtual ~EntityComponent() = default;
// other staff
};
class TransformComponent : public EntityComponent
{
public:
inline TransformComponent() : EntityComponent(ComponentType::TRANSFORM) { }
// other staff
};
int main()
{
EntityComponent* ec = GetComponentById(0);
if (ec->GetType() == ComponentType::TRANSFORM)
{
std::cout << "ec is transform component" << std::endl;
TransformComponent* tc = static_cast<TransformComponent*>(ec);
tc->GetPosition();
tc->GetScale();
}
}
有几种方法可以将 TransformComponent 添加到映射中,如 std::unique_ptr<EntityComponent>
:
std::unordered_map<int, std::unique_ptr<EntityComponent>> components;
// emplace raw pointer
int id = 0;
components.emplace(id, new TransformComponent());
// release unique_ptr of derived class and emplace
auto tc = std::make_unique<TransformComponent>();
components.emplace(id, tc.release());
// create unique_ptr of base class and move it to map
std::unique_ptr<EntityComponent> ec(new TransformComponent());
components.insert(id, std::move(ec));
我正在制作游戏引擎,目前正在研究实体组件系统。这里有一些代码可以更好地理解:
class Entity {
public:
...
void AddComponent(EntityComponent component, unsigned int id){
m_Components.insert({id, component});}
EntityComponent GetComponent(unsigned int id)
{
auto i = m_Components.find(id);
ASSERT(i != m_Components->end()); // ASSERT is a custom macro
return i->second;
}
private:
...
std::unordered_map<unsigned int, EntityComponent> m_Components;
};
也是一个简单的parent class EntityComponent,只处理ID(我跳过了一些代码,因为它在这个问题中无关紧要):
class EntityComponent {
public:
...
private:
unsigned int m_EntityID;
size_t m_PoolIndex;
};
它是 children。其中之一是 TransformComponent:
class TransformComponent : public EntityComponent {
public:
TransformComponent(unsigned int entityID, unsigned int poolID = 0, glm::vec2 position = glm::vec2(1.0,1.0), glm::vec2 scale = glm::vec2(1.0,1.0), float rotation = 0) : EntityComponent(entityID, poolID),m_Rotation(rotation), m_Scale(scale), m_Position(position){}
inline glm::vec2 GetPosition() { return m_Position; }
inline glm::vec2 GetScale() { return m_Scale; }
private:
glm::vec2 m_Position;
float m_Rotation;
glm::vec2 m_Scale;
};
所以问题是我想创建一个实体,向它添加一个 TransformComponent 并使用它的函数 GetPosition()、GetScale()。
创建实体并添加 TransformComponent 有效,但是当我想使用 GetComponent(id) 时,它 returns parent class EntityComponent, 所以这意味着我不能使用像 GetPosition() 等函数
如何更改代码,以便我可以将 EntityComponent 的不同 children 添加到 unordered_map 并使用它们的 ID 接收它们并使用它们public 方法?
如前所述,第一个问题是你应该存储EntityComponent*或EntityComponent的智能指针,以防止派生对象数据在分配给基时被擦除class。因此,容器类型将是以下之一:
std::unordered_map<int, EntityComponent*>
std::unordered_map<int, std::unique_ptr<EntityComponent>>
std::unordered_map<int, std::shared_ptr<EntityComponent>>
此外,应将虚拟析构函数添加到 EntityComponent 以正确删除派生的 classes:
class EntityComponent
{
virtual ~EntityComponent() = default;
// other stuff ...
};
何时可以使用 dynamic_cast 检查 EntityComponent 是否实际上是一个 TransformComponent:
EntityComponent* ec = GetComponentById(id); // suppose that function returns TransformComponent or EntityComponent
TransformComponent* tc = dynamic_cast<TransformComponent*>(ec);
if (tc != nullptr)
{
std::cout << "tc is transform component" << std::endl;
tc->GetPosition();
tc->GetScale();
}
但可能对于游戏引擎来说 dynamic_cast 在某些情况下可能会很慢。如果您知道从 EntityComponent 派生的所有 classes,您可以添加 Type 字段来检查实际对象类型,然后使用在编译时工作的 static_cast 转换为它:
enum class ComponentType
{
BASE,
TRANSFORM,
// other types
};
class EntityComponent
{
ComponentType _type;
public:
inline ComponentType GetType() const { return this->_type; }
inline EntityComponent(ComponentType type = ComponentType::BASE) : _type(type) { }
virtual ~EntityComponent() = default;
// other staff
};
class TransformComponent : public EntityComponent
{
public:
inline TransformComponent() : EntityComponent(ComponentType::TRANSFORM) { }
// other staff
};
int main()
{
EntityComponent* ec = GetComponentById(0);
if (ec->GetType() == ComponentType::TRANSFORM)
{
std::cout << "ec is transform component" << std::endl;
TransformComponent* tc = static_cast<TransformComponent*>(ec);
tc->GetPosition();
tc->GetScale();
}
}
有几种方法可以将 TransformComponent 添加到映射中,如 std::unique_ptr<EntityComponent>
:
std::unordered_map<int, std::unique_ptr<EntityComponent>> components;
// emplace raw pointer
int id = 0;
components.emplace(id, new TransformComponent());
// release unique_ptr of derived class and emplace
auto tc = std::make_unique<TransformComponent>();
components.emplace(id, tc.release());
// create unique_ptr of base class and move it to map
std::unique_ptr<EntityComponent> ec(new TransformComponent());
components.insert(id, std::move(ec));