【问题标题】:Function returning dynamicly casted pointer, return pointer as if it wasn't casted at all函数返回动态转换的指针,返回指针,就好像它根本没有被转换一样
【发布时间】:2019-11-09 15:05:01
【问题描述】:

我有一个Component 的基类和多个派生类,例如MeshComponentEntity 类将所有 Components 存储为 std::shared_ptr 中的 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 是一个包含所有组件类型的枚举类。 问题是当我在我的主文件中调用这个函数返回指针时,我只是指向基类,我不能调用特定于某些组件的函数。

我尝试将这些指针投射到我的主文件中,如下所示: std::dynamic_pointer_cast&lt;Engine::MeshComponent&gt;(testEntity.GetComponent(Engine::ComponentType::Mesh))-&gt;SetVertexArray(m_VertexArray);

但这会返回我的内存访问冲突。

【问题讨论】:

    标签: c++ polymorphism shared-ptr dynamic-cast downcast


    【解决方案1】:

    你的代码有什么问题?

    您的函数将返回一个指向基类的共享指针。这是您定义的返回类型,因此您需要像以前一样向下转换。

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

    你需要重新定义你的函数并使用一个值返回类型,删除const std::shared_ptr&lt;Component&gt;后面的&amp;

    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。您可以在线玩它,并进行实验:在返回类型中添加 & 将在此处导致运行时错误。

    多态不需要强制转换

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

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

    • 在这里,您在函数中进行了动态转换,但由于函数的返回类型是编译时,您的动态转换会立即恢复。这就是为什么您需要对返回的指针进行(有风险的)向下转换。
    • 没有办法用模板做到这一点。因为模板也基于编译时类型。

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

    推荐:

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

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

    【讨论】:

    • 那么如何定义正确的返回类型以便我可以使用GetComponent() 函数呢?这种分两步的方法也给我带来了内存访问冲突
    • @Szahu 正如我所展示的:您需要在返回类型之后删除 & !
    • 好吧,但我可以让它像这样工作吗:testEntity.GetComponent(Engine::ComponentType::Mesh)-&gt;//Function 而不是强制转换。我想要 GetComponent() 函数来处理所有的铸造。删除“&”对我不起作用
    • @Szahu:照顾所有 what 演员?你现在没有投射任何东西。或者更确切地说,您正在投射然后撤消它。您是否正在尝试创建一个基于参数返回不同类型的函数?因为那是不允许的。
    • @Szahu 的漏洞在于函数的返回类型是在编译时定义的。如果你返回一个指向组件的共享指针,你不能直接使用它,就好像它是一个网格组件一样。在您的 GetCoponent() 中,您装载了一些铸件,但这些铸件会立即重新铸造以匹配返回类型。问题是你是否不能让 Component 更加多态,这样用户就不需要知道它是哪种组件。这会让我更方便,并且可以摆脱所有这些铸造问题。
    猜你喜欢
    • 2022-11-21
    • 2012-06-07
    • 2022-01-17
    • 1970-01-01
    • 2012-05-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多