2010-04-23 127 views
0

我有一个C++项目QT开发。我正在运行的问题是我想要一个基类,我的所有属性类都继承,以便我可以将它们全部存储在一起。现在,我有:C++财产类结构

class AbstractProperty 
     { 
     public: 
      AbstractProperty(QString propertyName); 
      virtual QString toString() const = 0; 
      virtual QString getName() = 0; 
      virtual void fromString(QString str) = 0; 
      virtual int toInteger() = 0; 
      virtual bool operator==(const AbstractProperty &rightHand) = 0; 
      virtual bool operator!=(const AbstractProperty &rightHand) = 0; 
      virtual bool operator<(const AbstractProperty &rightHand) = 0; 
      virtual bool operator>(const AbstractProperty &rightHand) = 0; 
      virtual bool operator>=(const AbstractProperty &rightHand) = 0; 
      virtual bool operator<=(const AbstractProperty &rightHand) = 0; 
     protected: 
      QString name; 
     }; 

然后我采取类如PropertyFloat和PropertyString和基于这样的假设比较运营商,只有字符串被用绳子等相比,提供的实现。但是这个问题是不会有什么错误编译时抛出,如果我做了

if(propertyfloat a < propertystring b) 

但是我实现运营商的每一个派生类依赖于他们两个是相同的派生类。所以我的问题是我不知道如何实现一个属性结构,以便我可以让它们都从一些基本类型继承,但代码就像我上面会引发编译时错误。

如何可以做到这一点任何想法?对于那些熟悉QT我也试过用用的QVariant一个实施然而的QVariant没有运营商<和>只有在一些派生类的本身定义,因此它没有工作了。

什么我的最终目标是,是能够泛指性。我有一个元素类,其中包含一个字符串'name'作为键和AbstractProperty作为值的属性的散列图。我希望能够在属性上进行一般操作。即如果我想获得给定字符串名称的属性的最大值和最小值,我拥有完全通用的方法,将从每个元素中提取相关的AbstactProperty,并查找最大/最小值,而不管类型是什么。所以属性虽然最初声明为PropertyFloat/PropertyString,但它们将一般保持不变。

+1

你在做什么你的财产类?你从中得到什么好处? – 2010-04-23 18:45:26

回答

1

另一种解决方案是使用Curiously Recurring Template Pattern。这允许模板化类根据派生类定义比较运算符。

例子:

template <class Descendant> 
struct Numeric_Field 
{ 
    Descendant m_value; 

    bool operator==(const Descendant& d) 
    { 
     return m_value == d.value; 
    } 
    bool operator!=(const Descendant& d) 
    { 
     return !(*this == d); 
    } 
    bool operator< (const Descendant& d) 
    { 
     return m_value < d.m_value; 
    } 
    bool operator<=(const Descendant& d) 
    { 
     return (*this < d) || (*this == d); 
    } 
    bool operator> (const Descendant& d) 
    { 
     return !(*this <= d); 
    } 
    bool operator>=(const Descendant& d) 
    { 
     return !(*this < d); 
    } 

protected: 
    Numeric_Field(const Descendant& new_value = 0) 
    : m_value(new_value) 
    { ;} 
}; 

这可能是做了个比较通用的通过使用纯虚保护getter和setter方法代替m_value

template <class Descendant_Type> 
struct Numeric_Field_2 
{ 
    virtual const Descendant_Type get_value(void) const = 0; 
    virtual void      set_value(const Descendant& new_value) = 0; 

    bool operator==(const Descendant_Type& dt) 
    { 
     return get_value() == dt.get_value(); 
    } 
    bool operator< (const Descendant_Type& dt) 
    { 
     return get_value() < dt.get_value(); 
    } 
}; 

在这一点上,你可以绕过在需要比较的地方,指向Numeric_Field。我相信这只是一个打字保护程序。

5

如果有什么,而不是使之类的比较运营商会员,你让他们的全球职能?

如果你这样做,那么你可以为每个给定类型和控制操作,其类型可以互相比较:

bool operator==(const PropertyFloat &leftHand, const PropertyFloat &rightHand); 
bool operator==(const PropertyString &leftHand, const PropertyString &rightHand); 

编译器会抱怨任何时候,你这样做:

if(propertyfloat a == propertystring b) 
现在

,为了获得必要的私有数据的比较,使派生类的这些全局函数的“朋友”。

+0

+1用于提示全局功能。全局函数也可以使用模板进行概括。 – 2010-04-23 20:51:09

+0

这是一个很好的方法,我可能最终不得不走这条路,但我的主要犹豫是,那么我不能再将所有的信息放在元素类中。或者,如果我把它作为一般的基类,那么我必须有一些大的swtich铸造语句才能达到正确的等级,以便让操作员失去意识。 – 2010-04-26 15:51:40

+0

也许你应该检讨你的设计。平等和排序比较只应在特定(叶)类上执行。 – 2010-04-26 16:25:21

2

我在设计中的一个也有类似的问题,直到我沉思了它。我意识到在基类中有比较运算符是错误的。正如你所发现的,基类中的比较方法是比较任何后代类的组合。这是糟糕的噶玛。

更好的办法是在子孙类定义比较运算符。这将帮助编译器防止使用不同类型的操作员的人员。这样做的副作用是一个不能由解引用指针的基类(这将是一件好事)比较对象。

另一个有用的工具是加速比较模板。寻找equality_comparable

+0

如果我最终没有在基类中声明的比较,并且我经常不得不投入后代类。这意味着当我想进行比较以确定要抛弃的类型时,需要使用大型开关/案例语句。你是如何看待这个的?我真的想保持我的元素类只有一个属性的hasmap,所以我们可以拥有任何类型的属性,但这需要将其一般存储为基础。 – 2010-04-26 15:50:25

+0

我绝对需要时只传递基类指针。很少有我不得不沮丧的情况。此外,我所拥有的唯一“开关/外壳”声明在工厂。我尝试将通用代码传播到基类或父类中。我有通用的'Field'和'Record'类,使数据库接口更简单。我有特定的'Recipe','Ingredient'和'Menu'类。为了使用数据库,'Ingredient'从Record继承,所以数据库将它视为一条记录,而不需要知道具体细节。 – 2010-04-26 16:23:31