钻石继承问题

Sta*_*ked 1 c++ diamond-problem

为了好玩,我正在为Windows的XUL实现工作.在XUL中,UI元素可以用XML编写,如下所示:

<window width="800" height="600"></window>
Run Code Online (Sandbox Code Playgroud)

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

#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;
}
Run Code Online (Sandbox Code Playgroud)

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

编辑 关于"is-a"vs"has-a",以及"赞成组合而不是继承"的评论我这样说:

  • 这里继承有一个优点.如果Element继承Width,则强制它实现getWidth和setWidth方法.因此添加属性意味着Element的界面的"自动"更新.
  • 我最初将这些类命名为AttributeController,WidthController和HeightController,但我发现它们太冗长了.你可以说我的Element是一个属性控制器.(好吧,这很蹩脚,但并非不真实!)
  • 进一步证明:宽度和高度的定义不包含任何数据成员.Element类实际上它们.Width和Height类仅提供接口.所以这更像是一种可以做的关系.

Jim*_*uck 6

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

  • 同意100%......继承是错误的工具. (2认同)