首页 > 解决方案 > 用于存储的抽象基类的替代方案

问题描述

我想知道运行时多态性的可用替代方案,特别是在我的类之间使用公共基类的替代方案,以便存储派生类的实例并与之交互。RTP 存在通过 vtable 查找间接的缺点,并且还强制派生类存储为指针,因此通常必须动态分配它们。据我所知,这阻碍了内联和编译器优化。

简而言之,我不想要这个:

class Animal
{
public:
    virtual void noise() const = 0;
};

class Dog : public Animal
{
public:
    virtual void noise() override const {std::cout<<"Woof!\n";};
};

class Cat : public Animal
{
public:
    virtual void noise() override const {std::cout<<"Meow!\n";};
};

//...

std::vector<Animal*> animals;

我想要这样的东西:

class Dog
{
public:
    void noise() const {std::cout<<"Woof!\n";};
};

class Cat
{
public:
    void noise() const {std::cout<<"Meow!\n";};
};

//...

std::vector<*Things that have noise()*> animals;

标签: c++polymorphismc++17static-polymorphism

解决方案


在您的示例中,我无法帮助您,但是对于具有数据成员的类,这里有一个优化。

假设我们要创建一个弹幕游戏。

在一种天真的方法中,我们需要一个类Bullet

class Bullet {
public:
    void draw(Renderer& r);
    virtual void update_logic(float delta);
    void run_physics();
protected:
    float vx, vy; //velocity
private:
    float x, y; //position
    //...
};

因此,update_logic将调用该方法来设置子弹的速度,然后run_physics将此速度应用于位置。

修改逻辑需要多态性,所以你的集合变成了std::vector<Bullet*>(或std::vector<std::unique_ptr<Bullet> >在现代 c++ 中)。

然后,在您的主循环中,首先更新逻辑,然后更新物理,然后绘制子弹。

当然,由于指针取消引用和 vtables 等,它是低效的。

你可以做的是:

class Bullet {
public:
    void draw(Renderer& r);
    void update_logic(float delta) { logic->update(delta); };
    void run_physics();
    void setVelocity(float, float);
    void setLogic(BulletLogic*);
private:
    float x, y, vx, vy;
    BulletLogic* logic;
};

所以现在,您可以使用std::vector<Bullet>,并且仅在更新逻辑时才会发生间接,但在运行物理或绘制子弹时不会发生。


推荐阅读