钻石继承问题

时间:2009-09-27 16:33:27

标签: c++ diamond-problem

为了好玩,我正致力于Windows的XUL实现。在XUL中,UI元素可以用XML编写:

<window width="800" height="600"></window>

我正在考虑获取和设置元素属性的系统。它工作得很好,但我不确定钻石继承的使用是否有潜在危险。我在下面发布了一个完整的代码示例:

#include <boost/lexical_cast.hpp>
#include <string>
#include <map>


class Attribute
{
public:
    virtual void get(std::string & outValue) = 0;
    virtual void set(const std::string & inValue) = 0;

    static int String2Int(const std::string & inString)
    {
        return boost::lexical_cast<int>(inString);
    }

    static std::string Int2String(int inValue)
    {
        return boost::lexical_cast<std::string>(inValue);
    }
};


class Width : public Attribute
{
public:
    Width(){}

    virtual void get(std::string & outValue)
    {
        outValue = Int2String(getWidth());
    }

    virtual void set(const std::string & inValue)
    {
        setWidth(String2Int(inValue));
    }

    virtual int getWidth() const = 0;

    virtual void setWidth(int inWidth) = 0;
};


class Height : public Attribute
{
public:
    Height(){}

    virtual void get(std::string & outValue)
    {
        outValue = Int2String(getHeight());
    }

    virtual void set(const std::string & inValue)
    {
        setHeight(String2Int(inValue));
    }

    virtual int getHeight() const = 0;

    virtual void setHeight(int inHeight) = 0;
};

class Element : public Width,  // concerning the is-a vs has-a philosophy
                public Height  //   => see my note below
{
public:
    Element() :
        mWidth(0),
        mHeight(0)
    {
        // STATIC CAST NEEDED HERE OTHERWISE WE GET COMPILER ERROR:
        // error C2594: '=' : ambiguous conversions from 'Element *const ' to 'Attribute *'
        mAttrControllers["width"] = static_cast<Width*>(this);
        mAttrControllers["height"] = static_cast<Height*>(this);
    }

    void setAttribute(const std::string & inAttrName, const std::string & inAttrValue)
    {
        Attributes::iterator it = mAttrControllers.find(inAttrName);
        if (it != mAttrControllers.end())
        {
            Attribute * attribute = it->second;
            attribute->set(inAttrValue);
        }
    }

    std::string getAttribute(const std::string & inAttrName)
    {
        std::string result;
        Attributes::iterator it = mAttrControllers.find(inAttrName);
        if (it != mAttrControllers.end())
        {
            Attribute * attribute = it->second;
            attribute->get(result);
        }
        return result;
    }

    virtual int getWidth() const
    {
        return mWidth;
    }

    virtual void setWidth(int inWidth)
    {
        mWidth = inWidth;
    }

    virtual int getHeight() const
    {
        return mHeight;
    }

    virtual void setHeight(int inHeight)
    {
        mHeight = inHeight;
    }

private:
    typedef std::map<std::string, Attribute *> Attributes;
    Attributes mAttrControllers;
    int mWidth;
    int mHeight;
};


int main()
{
    Element el;
    el.setAttribute("width", "800");
    el.setAttribute("height", "600");
    int w = el.getWidth();
    int h = el.getHeight();
    return 0;
}

我认为没关系,因为基类Attributes没有数据成员,所以不会出现冲突。但我想我会和社区核实一下。非常感谢您的见解!

修改的 关于“is-a”vs“has-a”,以及“赞成组合而非继承”的评论,我有这样的说法:

  • 有 这是继承的一个优点。 如果Element继承了Width,那么它就是 被迫实现getWidth和 setWidth方法。所以加一个 属性意味着'自动' 更新Element的界面。
  • 我最初将这些类命名为 AttributeController,WidthController 和HeightController,但我找到了 他们太冗长了。你可以说我的Element是一个属性控制器。 (好吧,这很蹩脚,但并非不真实!)
  • 进一步证明:宽度和高度的定义 不包含任何数据成员。该 元素类实际上具有它们。 仅限宽度和高度类 提供接口。所以它更像是一个 can-do 的关系。

4 个答案:

答案 0 :(得分:6)

在您的场景中,Element可能不应该从Width和Height继承,而是Width和Height应该是element的数据成员。它的构成与is-a相反,因为可以说元素不是宽度或高度,而是由宽度和高度组成(也可能是其他一些东西)。

答案 1 :(得分:2)

元素应继承宽度 ,如果您需要将元素对象用作宽度 1。 Inheritance is not for code reuse

看看boost :: program_options库可能值得一试。我喜欢他们用于注册房产的奇特方式。

答案 2 :(得分:0)

如果我要这样做,我会在Attribute上进行虚拟继承。我认为这不会有什么重要意义,但如果它最终成为问题,那么这将最大限度地减少重复。

答案 3 :(得分:-1)

首先,如果您面临钻石问题,请使用virtual inheritance,即在基类上使用public virtual而不是public

其次,由于有两个实现,因此您的get - 和set - 将会执行的操作并不明确。我使用多重继承的唯一时间是扩展纯虚拟类(也就是接口)。这是Java不支持多重继承的一个很好的理由。

第三,更重要的是,这似乎是在面向对象时对继承(“是a”)与聚合(“has a”)的误解之间的典型案例。在确定某个类是否应该继承另一个类时,您可以使用两个非常简单的指南。如果你有继承A类的A类和B类,则句子“A是a B”应该是有意义的。如果“A有一个B”听起来更好,你应该考虑让B成为A的成员。

在你的情况下,句子“元素是高度”和“元素是宽度”真的没有意义。 “元素有高度”非常有意义。