c++ 当我只有一个包含父类的列表时,对不同的子类使用函数重载

xqnpmsa8  于 2022-11-27  发布在  其他
关注(0)|答案(2)|浏览(95)

首先,我的代码的一个小解释,把上下文的问题:
我有一个类,负责在屏幕上绘制东西,我使用一个重载函数来绘制不同类型的可绘制实体,函数看起来像这样:

draw(entityType1* name);
draw(entityType2* name);
draw(entityType3* name);
...

所有实体类都派生自父“实体”类
我写了一个名为“Scene”的类,它有一个包含场景中所有可绘制对象的实体列表,我将该场景对象传递给负责在屏幕上绘制内容的类。
我们的想法是遍历列表并使用函数重载来绘制列表上不同类型的对象,但是由于列表只包含Entity类型的对象,因此我不能真正使用函数重载,因为它只适用于子类
我在找一个和这个类似的代码

void Painter::draw(Scene* scene) {
    std::list<Entity*> drawables = scene->getDrawables();
    for (auto it = drawables.begin(); it != drawables.end(); it++) {
        draw(*it); //Should apply the correct overload
    }
}

这段代码显然不起作用,因为我没有任何接受实体类型的draw()函数。我总是可以要求实体类型逐个执行,但这违背了使用重载函数的目的,也违反了“告诉,不要问”的原则。
我可能做了一些非常错误的事情,但我真的不知道如何进行,这就是为什么我问这个问题,我想知道什么是正确的方法来解决这个问题,同时尊重OOP原则,可能性是开放的,一切都在表上的变化我的代码。
先谢了

gab6jxml

gab6jxml1#

你可以使用访问者模式来解决这个问题,这个模式将函数调用委托给对象本身,所以你不需要使用对象的类型来调用正确的函数。
以下是您可以实现它的方法:

void Painter::draw(Scene* scene) {
    std::list<Entity*> drawables = scene->getDrawables();
    for (auto it = drawables.begin(); it != drawables.end(); it++) {
        (*it)->accept(this); // call virtual function
    }
}
// in Entity class
void Entity::accept(Painter* painter) {
    painter->draw(this);
}
// in entityType1 class
void entityType1::accept(Painter* painter) {
    painter->draw(this);
}
// etc...

访问者模式的更高级实现可以在这里找到:Implementing the visitor pattern using C++ Templates

ua4mk5z4

ua4mk5z42#

如果我没理解错你的问题,你想要的是在Painter类中有draw的逻辑,而原始的Entity类型保持不变。你可以通过类型擦除来实现这一点:

struct DrawableEntityView
{
    template<std::derived_from<Entity> EntityType>
    DrawableEntityView(const EntityType& entity)
    : p_entity(&entity)
    , drawn_by_impl{
        [](const Entity* entity, const Painter& painter) {
            painter.draw(static_cast<const EntityType*>(entity));
        }
    }
    {}

    void drawn_by(const Painter& painter) const
    { drawn_by_impl(p_entity, painter); }
private:
    const Entity* p_entity;
    void (*drawn_by_impl)(const Entity*, const Painter&);
};

它的本质是DrawableEntityView可以用Entity的任何派生类来构造。

EntityType1 e1;
DrawableEntityView e1_view{e1};

这将在e1_view中存储一个Entity*,而函数指针drawn_by_impl将存储一个lambda,该lambda可以将p_entity转换为基于构造它所使用的类型,并使用它调用Painter::draw
现在你可以有这样的代码:

EntityType1 e1;
EntityType2 e2;
EntityType3 e3;

std::vector<DrawableEntityView> vec{e1, e2, e3};

Painter painter;
for(auto& entity: vec)
{
    entity.drawn_by(painter);
}

当然,您可以在Painter中添加一个成员函数,如下所示:

void Painter::draw(const DrawableEntityView& entity) const
{ entity.drawn_by(*this); }

现在在for循环中,可以有:

for(auto& entity: vec)
{
    painter.draw(entity);
}

下面是一个演示:https://godbolt.org/z/fPM4nv3xz
我特意将其设为 view,但您也可以将DrawableEntity::p_entity设为所属指针,并删除一些常量。现在您可以让场景直接将实体存储为DrawableEntity

相关问题