函数 returning 动态转换指针,return 指针就好像它根本没有被转换一样

Function returning dynamicly casted pointer, return pointer as if it wasn't casted at all

我有一个 Component 的基数 class 和多个派生 class 的 MeshComponentEntity class 将所有 Components 存储为 std::shared_ptr in std::unordererd_map caaled m_Components,因此我创建了以下函数以从地图中获取组件:

const std::shared_ptr<Component>& Entity::GetComponent(ComponentType type)
{   
    switch (type)
    {
        case ComponentType::None:
            EG_CORE_ASSERT(false, "Component of type None is not supported!");
            return nullptr;
        case ComponentType::Transform:
            return std::dynamic_pointer_cast<TransformComponent>(m_Components[ComponentType::Transform]);
        case ComponentType::Mesh:
            return std::dynamic_pointer_cast<MeshComponent>(m_Components[ComponentType::Mesh]);
    }
}

ComponentType 是一个包含所有组件类型的枚举 class。 问题是,当我在我的主文件 returned 指针中调用此函数时,我只是指向基 class 并且我无法调用特定于某个组件的函数。

我尝试的是在我的主文件中投射这些指针,如下所示: std::dynamic_pointer_cast<Engine::MeshComponent>(testEntity.GetComponent(Engine::ComponentType::Mesh))->SetVertexArray(m_VertexArray);

但是这个 return 我的内存访问违规。

你的代码有什么问题?

您的函数将 return 指向基 class 的共享指针。是你定义的 return 类型,所以你需要像你一样向下转型。

但是你在这里做错了:你 return 一个引用,这个引用将指向一个临时共享指针,一旦你 return 从 GetComponent()导致 UB。

您需要重新定义函数并使用值 return 类型,删除 const std::shared_ptr<Component> 后面的 &

const std::shared_ptr<Component> Entity::GetComponent(ComponentType type)

如果您向下转换为正确的类型,这应该可以解决您的问题。

您的代码有哪些地方可以改进?

但是你应该接受向下转换可能出错的事实。 dynamic_pointer_cast 的全部目的是允许这种安全检查。所以而不是:

std::dynamic_pointer_cast<Engine::MeshComponent>(testEntity.GetComponent(Engine::ComponentType::Mesh))->SetVertexArray(m_VertexArray);

分两步完成:

auto pm = std::dynamic_pointer_cast<Engine::MeshComponent>(testEntity.GetComponent(Engine::ComponentType::Mesh)); 
if (pm) 
    pm->SetVertexArray(m_VertexArray);
else std::cout << "Oh oh ! Something went wrong"<<std::endl; 

这里是一个 online demo 的极简主义例子。你可以在线玩玩它,并进行实验:将 & 添加到 return 类型会导致此处出现运行时错误。

多态性不需要转换

如果您有多态代码并且必须进行大量转换,则说明设计有问题。

首先,没有办法创建一个多态 Getcomponent() 来让您立即调用多态类型不存在的函数:

  • 此处您在函数中进行了动态转换,但由于函数的 return 类型是编译时类型,因此您的动态转换会立即恢复。这就是为什么您需要使用指针 returned 进行(有风险的)向下转型。
  • 无法使用模板来完成。因为模板也是基于编译时类型的。

您可以做的是定义三个不同的函数,每个函数 return 都是预期的类型。但是你必须格外小心,因为以这种方式获取指针,不确定首先找到指针,并且不确定动态转换是否成功,可能会导致 nullptr 的 rdereferencing,这是 UB并可能使您的软件崩溃。

建议:

尽量将Component的成员函数设计成多态的,这样用户在调用函数时就不必知道是不是mesh了。向下转型应该是个例外。

如果不可能,并且您仍然更喜欢使用专门的功能(例如 GetMeshComponent())(从而使您的设计的可扩展性大大降低),那么您应该预见到一些异常处理如果此专用函数无法提供预期的指针。