C++ dynamic_cast dowcast 失败

C++ dynamic_cast dowcast fails


在用 C++ 编写我的第一个大项目时,我遇到了一个我无法单独使用 google 和文档解决的问题。

我想不通为什么这个 dynamic_cast 失败了,即使 r 指向一个 MeshRenderer 对象。

for (RenderEventConsumer* r : d->getConsumers())
{
    glUseProgram(mPickingShader->apiID);
    MeshRenderer* m = dynamic_cast<MeshRenderer*>(r); //returns nullptr
    if (m)
    {
        glUniform1ui(uPickingID, m->getOwner()->getID());
        m->getMesh()->getUtillityBuffer().draw();
    }
}

classRenderEventConsumer 有一个虚拟方法,是 MeshRenderer 的基础。

class MeshRenderer : public Component {...}
class Component : public GameObject {...}
class GameObject : protected TickEventConsumer, protected RenderEventConsumer, protected PhysicsTickEventConsumer {...}

根据Visual Studio,r 的 vftable 是正确的。

PS:这是我在 Whosebug 上的第一个问题,如果我违反了任何准则或缺少相关信息,请告诉我。

编辑:虽然我现在知道答案了,但为了清楚起见,我用一个独立的例子重现了错误:

#include <vector>
#include <iostream>

class RenderEventConsumer
{
    virtual void onRender() {};
};

class RenderEventDispatcher
{
    std::vector<RenderEventConsumer*> mConsumers;
public:
    const std::vector<RenderEventConsumer*>& getConsumers()
    {
        return mConsumers;
    }
    void registerRenderEventConsumer(RenderEventConsumer* consumer)
    {
        mConsumers.push_back(consumer);
    }
};

class GameObject : protected RenderEventConsumer {}; //changing this to public fixes dynamic_cast
class Component : public GameObject {};
class MeshRenderer : public Component 
{
public:
    void setup(RenderEventDispatcher& d) 
    {
        d.registerRenderEventConsumer(this);
    }
    void onRender() override { }
};

int main()
{
    RenderEventDispatcher d;
    MeshRenderer* pt = new MeshRenderer();
    pt->setup(d);
    
    for (RenderEventConsumer* r : d.getConsumers())
    {
        MeshRenderer* m = dynamic_cast<MeshRenderer*>(r);
        if (m)
        {
            std::cout << "not nullptr\n";
        }
        else
        {
            std::cout << "nullptr\n";
        }
    }
}

仅仅因为 RenderEventConsumer 是基数 MeshRenderer,这并不意味着从 RenderEventConsumer 派生的所有 类 也(派生自)MeshRenderer.

如果 MeshRenderer 不是指向对象的动态类型(的基础),则 dynamic_cast 将 return 为空。

这是一个可能发生的例子:

struct another_derived : RenderEventConsumer {} instance;
RenderEventConsumer* r = &instance;

// null, even through r is RenderEventConsumer*
dynamic_cast<MeshRenderer*>(r);

如果你发现你需要使用dynamic_cast,那意味着设计被破坏了。考虑改用访问者模式。

感谢 Kaldrr。
解决方案是公开派生自 RenderEventConsumer。

class GameObject : public TickEventConsumer, public RenderEventConsumer, public PhysicsTickEventConsumer {...}