【问题标题】:Defining a type only by its inheritance from other classes仅通过从其他类继承来定义类型
【发布时间】:2013-10-17 17:13:40
【问题描述】:

我正在 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 类,并且绘图函数将调用私有 onDraw 本身,然后调用它的子级。但是,许多 SFML 原生类,例如 sf::Text,都有一个私有的绘制函数。所以,我不能创建一个像

这样的类
class Text: public sf::Text, public SceneNode

然后将其放入可视化树中。对于这些原生类,无论如何我都不需要它们绘制子类,我只想能够将它们作为叶节点添加到可视化树中。问题的症结在于视觉树的每个成员都需要继承自sf::Drawablesf::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。这可能吗?

【问题讨论】:

    标签: c++ inheritance sfml


    【解决方案1】:

    但是,许多 SFML 原生类,例如 sf::Text,都有一个私有的绘制函数

    这并不完全正确。由于sf::Drawable::draw 函数受到保护,所以sf::Textdraw 方法也受到保护。这是 C++ 的复杂规则之一。

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

    class Text: public sf::Text, public SceneNode
    

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

    在绘制这些子项时,我会对每个项进行动态强制转换,看看它是否是一个场景节点,然后调用一个绘制函数让场景节点绘制它的子项。

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

    但让我们回答你的基本问题:

    这种组合语法意味着每个元素都必须从 sf::Drawable 和 sf::Transformable 派生。这可能吗?

    没有。但无论如何你都可以做更简单的事情。

    不要让Text 继承自sf::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);
        }
    };
    

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

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

    【讨论】:

      【解决方案2】:

      在不了解 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)。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2020-06-06
        • 1970-01-01
        • 2013-02-09
        • 1970-01-01
        • 2020-09-07
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多