c++ property class structure
I have a c++ project being developed in QT. The problem I'm running in to is I am wanting to have a single base class that all my property classes inherit from so that I can store them all together. Right now I have:
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;
};
then I am implementing classes such as PropertyFloat and PropertyString and providing implementation for the comparator operators based on the assumption that only strings are being compared with strings and so on. However the problem with this is there would be no compiletime error thrown if i did
if(propertyfloat a < propertystring b)
however my implementation of the operators for each derived class relies on them both being the same derived class. So my problem is I cant fig开发者_JAVA技巧ure out how to implement a property structure so that I can have them all inherit from some base type but code like what I have above would throw a compile time error.
Any ideas on how this can be done? For those familiar with QT I tried using also a implementation with QVariant however QVariant doesn't have operators < and > defined in itself only in some of its derived classes so it didn't work out.
What my end goal is, is to be able to generically refer to properties. I have an element class that holds a hashmap of properties with string 'name' as key and the AbstractProperty as value. I want to be able to generically operate on the properties. i.e. if I want to get the max and min values of a property given its string name I have methods that are completely generic that will pull out the associated AbstactProperty from each element and find the max/min no matter what the type is. so properties although initially declared as PropertyFloat/PropertyString they will be held generically.
What if instead of making the comparator operators members of the class you make them global functions?
If you did that, then you could make an operator for each given type and control which types can compare to each other:
bool operator==(const PropertyFloat &leftHand, const PropertyFloat &rightHand);
bool operator==(const PropertyString &leftHand, const PropertyString &rightHand);
The compiler would complain anytime that you did this:
if(propertyfloat a == propertystring b)
Now, in order to access the private data necessary for the comparison, make these global functions "friends" of the derived classes.
I had a similar problem in one of my designs until I meditated over it. I realized that having comparison operators in the Base class was wrong. As you have found out, a comparison method in the base class is comparing any combination of descendant classes. This is bad Karma.
A better approach is to define the comparison operators in the descendant classes. This will help the compiler prevent people using the operators with different types. The side effect of this is that one can't compare objects by dereferencing a pointer to the base class (which will be a good thing).
Another useful tool are the Boost comparison templates. Look for equality_comparable
.
Another solution is to use the Curiously Recurring Template Pattern. This allows a templated class to define comparison operators based on a derived class.
Example:
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)
{ ;}
};
This could be made a little more generic by replacing m_value
by using pure virtual protected setters and getters:
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();
}
};
At this point, you could pass around pointers to Numeric_Field
wherever a comparison is needed. I believe this is only a typing saver.
精彩评论