仅通过从其他类继承类型来定义类型

Defining a type only by its inheritance from other classes

本文关键字:类型 继承 定义 其他      更新时间:2023-10-16

我正在SFML中实现一个可视化树。SFML 包含两个重要的绘图类:sf::Drawablesf::Transformable 。如果这些捆绑在一起会很好,但它们不是。许多对象继承自两者,即:

class SFML_GRAPHICS_API Text : public Drawable, public Transformable
class SFML_GRAPHICS_API Sprite : public Drawable, public Transformable
class SFML_GRAPHICS_API Shape : public Drawable, public Transformable

对于我的可视化树,我有一个继承自 Drawable 和 Transformable 的 SceneNode 类,并且 draw 函数将调用私有 onDraw 本身,然后调用其子级。但是,许多 SFML 本机类(如 sf::Text)具有私有的绘制函数。所以,我不能创建一个这样的类

class Text: public sf::Text, public SceneNode

然后将其放入可视化树中。对于这些本机类,我无论如何都不需要它们来绘制子类,我只是希望能够将它们作为叶节点添加到可视化树中。问题的症结在于可视化树的每个成员都需要从sf::Drawable继承和sf::Tranformable。我需要能够定义一个继承自这两者的类型。如果我定义虚拟类

class LeafNode: public sf::Drawable, public sf::Tranformable { }

这似乎定义了我想要的类型。然后,SceneNode将包含std::vector<LeafNode*> m_children .在绘制这些子项时,我将对每个项目进行动态转换,以查看它是否是SceneNode,然后调用绘制函数,以便SceneNode绘制其子项。

但是,由于类型不兼容,以下代码无法编译:

LeafNode * node = new sf::Text("PLAY", font, 20);

理想情况下,我想定义类似的东西

std::vector<sf::Drawable, sf::Transformable*> m_children 

其中,这种虚构的语法意味着每个元素都必须从sf::Drawablesf::Transformable派生。这可能吗?

但是,许多 SFML 本机类(如 sf::Text)具有私有的绘制函数

这并不完全正确。由于sf::Drawable::draw功能受到保护,因此sf::Text draw方法也受到保护。这是C++的复杂规则之一。

所以,我不能创建一个这样的类

class Text: public sf::Text, public SceneNode

如果这样做,层次结构中将有两个sf::Drawablesf::Transformable基类,一个来自sf::Text,一个来自SceneNode。那就不好了。

绘制这些子项时,我将对每个项目进行动态投射,以查看它是否是 SceneNode,然后调用绘制函数,以便 SceneNode 绘制其子项。

我不推荐这样的设计。使用dynamic_cast通常表明您的软件设计不是那么好。(我不想在这个话题上离题太多,谷歌关于这个话题。

但是,让我们回答您的基本问题:

其中,这种虚构的语法意味着每个元素都必须从 sf::D rawable 和 sf::Transformable 派生。这可能吗?

不。但无论如何,你可以做更简单的事情。

与其让Textsf::TextSceneNode继承,不如将类定义为包装器。它可以像这样简单:

class Text : public SceneNode {
    sf::Text m_text;
public:
    sf::Text& get() { return m_text; }
    // Override SceneNode drawing method:
    virtual void onDraw(RenderTarget& target) const
        // Draw m_text:
        target.draw(m_text);
    }
};

不过,这个快速包装器有两个缺陷。它不使用SceneNode的可转换部分。b) 由于封装被get()破坏,因此用户可修改两个可转换的:一个来自SceneNode,一个来自sf::Text

对于 a),当您修复 b) 时,修复应该很简单。要修复 b),您必须使包装器稍微复杂一点:而不是这个丑陋的get(),编写方法来设置未链接到sf::Transformable的底层sf::Text的属性,例如 setColor .

在对 SMFL 一无所知的情况下(它可能提供更好的解决方案),我认为您可以实现这个向量。您只需要定义自己的指针包装器,它只接受指向从多个类型继承的对象的指针:

template <class T1, class T2>
struct special_pointer_wrapper
{
    T1* t1;
    T2* t2;
    template<class T>
    special_pointer_wrapper(T* p)
        : t1(dynamic_cast<T1*>(p))
        , t2(dynamic_cast<T2*>(p))
    {
       if ((p1==0) || (p2==0))
           throw "Error";
    }
    getT1 T1* () const { return t1; }
    getT2 T2* () const { return t2; }
};

此类接受任何指针,并确保其指向的类型派生自 T1 和 T2(即使它们看起来完全不相关)。如果它不是派生对象,则抛出。通过函数getT1()getT2(),您可以访问指向两个基类的指针。

请注意,由于dynamic_cast构造可能很慢,但类型的提取为 O(1)。