如何 'set' 反对多重继承中的特定基础 class?

How to 'set' object to a particular base class in multiple inheritence?

我正在尝试从 C++ 中的两个基础 classes 继承,它们具有相同的命名函数。我想要一个 class 到 'belong' 的对象到特定的基础 class,是否可以这样做?

我已经尝试过虚拟基础 class(不适合我的特定情况)并且还尝试过使用范围解析运算符但失败了。

如果可能的话,我想只使用一个 class 来做到这一点。

我已经提供了代码;

#include <SFML\Graphics.hpp>

class entity :public sf::CircleShape, public sf::RectangleShape {
public: 
    entity(int radius = 0) { setRadius(radius); }
    entity(sf::Vector2f size) { setSize(size); }
    float xspeed =0, yspeed =0;
};
entity ball(6);
entity block(10, 10);
window.draw(block);   // ambiguity error as both have base class sf::drawable
window.draw(ball);    // and draw function accepts sf::drawable

问题是我希望 "ball" 只是继承自 sf::CircleShape 而不是 sf::RectangleShape

我预计使用范围解析运算符会引用 circleshape class 或者我可能只是用错了。

编辑:我要解决的问题是我想在 window 上绘制 "ball" 和一个矩形块,当我尝试这样做时,我会看到有歧义错误,因为它们都是可绘制的(即 sf::RectangleShapesf::CircleShape 都继承自 sf::drawable

编辑(2):The inheritence diagram

所以函数window.draw();接受sf::drawable的任何对象作为参数。但是当我将圆形和矩形都继承给实体时,我收到一个歧义错误,说 base class is ambiguous.

我知道这是死亡钻石,但是使用虚拟基础 classes 是不可能的,因为它们是 SFML 库的一部分,我不想修改它们

而不是使用继承:

直接使用直接关联:

在您的实体 class 中,您希望拥有 RectangleShape 和 CircleShape ,因此不要同时将实体 class 设为 CircleShape 和 RectangeShape,最好考虑您的实体制作一个 CircleShape 和一个 RectangleShape 的。这样你可以在你的实体 class 实例化一个 RectangeShape 和一个 CircleShape 意味着 'entity' 有一个 Rectangle 和一个 CircleShape

这是正确的做法

问题分析

class entity :public sf::CircleShape, public sf::RectangleShape

这个声明说每个 entity 同时是一个 CircleShape 和一个 RectangleShape。这似乎不是你想要的。您似乎想要一个可以是 圆形 矩形的实体。所以这种继承不是正确的策略。

鉴于您正在添加水平和垂直速度,您似乎正在尝试向可以绘制的元素添加基本动画。动画是一个建立在绘图之上的抽象概念(动画必须被绘制,但并非所有的绘图都必须是动画的)。特定的移动形状将建立在动画之上,因此您理想的继承将更像是以下内容:

circle -> entity -> drawable

但是,也有一些缺点。一个缺点是这会强制所有圆圈成为动画实体,而一些圆圈可能只是在一个点上绘制。另外,圆圈和可绘制对象 classes 来自一个库,因此您不能弄乱那个特定的继承方案。

我能想到两个合理的选择。 (顺便说一句,"entity" 不是您的 class 的好名字,但为了保持一致性我会坚持使用它。您应该为 class。这将是一个很好的做法,因为命名有时是编写代码中最困难的部分之一。:) )

实体作为基础class

您可以将 entity 定义为基础 class,然后为每个形状定义一个新的 class。

             --> sf::CircleShape --> sf::Drawable
            /
MyCircle --<
            \
             --> entity

这可行,但如果 entity 需要调用 Drawable 中定义的函数,则效果不佳。并非不可能:side-cast 可以调用 Drawable 的函数。只是你必须考虑 side-cast 失败的情况,因为编译器无法捕捉到它。

作为成员的形状

我可能会做的是完全放弃继承。我不想说您的实体是一个圆圈(继承),而是采用您的实体有一个圆圈(成员资格)的方法。这种方法的一个好处是,不难将其扩展为说您的实体具有多种形状,并且都以相同的速度移动。不过,我现在会坚持使用单一形状。

这种方法的困难在于您不知道实体应该具有什么形状——它应该是圆形还是矩形?幸运的是,多态性可以很好地处理这种事情。

class entity {
    std::unique_ptr<sf::Drawable> shape; // <-- Polymorphism to the rescue!
    float xspeed = 0.0, yspeed = 0.0;

public:
    // Construct a circle.
    entity(int radius = 0) : shape(std::make_unique<sf::CircleShape>(radius)) {}

    // Construct a rectangle.
    entity(sf::Vector2f size) : shape(std::make_unique<sf::RectangleShape>(size)) {}

    // Something will go here to support drawing.
};

要支持绘图,有(至少)两个选项。如果用 entity 作为 drop-in 替代 Drawable 是合适的,定义隐式转换可能是合理的。

    operator const sf::Drawable &() const { return *shape; }

如果不需要隐式转换,可以将其标记为 explicit

如果您唯一需要将 entity 用作 Drawable 的时间是在您调用 window.draw() 时,您可能希望给 entity 一个 draw 方法以 window 作为参数。

    void draw(sf::RenderTarget & target, sf::RenderStates states) const
    { target.draw(*shape, states); }

这使得 Drawable 可供 RenderTarget::draw() 使用,同时在其他时间不可见(减少混乱)。