структура класса свойств c ++ - PullRequest
0 голосов
/ 23 апреля 2010

У меня есть проект на C ++, разрабатываемый в QT. Проблема, с которой я сталкиваюсь, заключается в том, что я хочу иметь один базовый класс, от которого наследуются все мои классы свойств, чтобы я мог хранить их все вместе. Прямо сейчас у меня есть:

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 из каждого элемента и находят max / min независимо от типа. поэтому свойства, хотя изначально они были объявлены как PropertyFloat / PropertyString, будут храниться в общем виде.

Ответы [ 3 ]

5 голосов
/ 23 апреля 2010

Что если вместо того, чтобы делать операторы сравнения членами класса, вы сделаете их глобальными функциями?

Если бы вы это сделали, вы могли бы создать оператор для каждого данного типа и указать, какие типы могут сравниваться друг с другом:

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

Компилятор будет жаловаться в любое время, что вы сделали это:

if(propertyfloat a == propertystring b)

Теперь, чтобы получить доступ к частным данным, необходимым для сравнения, сделайте эти глобальные функции «друзьями» производных классов.

2 голосов
/ 24 апреля 2010

У меня была похожая проблема в одном из моих проектов, пока я не размышлял над этим. Я понял, что иметь операторы сравнения в базовом классе было неправильно. Как вы узнали, метод сравнения в базовом классе сравнивает любую комбинацию классов-потомков. Это плохая карма.

Лучший подход - определить операторы сравнения в классах-потомках. Это поможет компилятору предотвратить использование людьми разных типов. Побочным эффектом этого является то, что нельзя сравнивать объекты, разыменовывая указатель на базовый класс (что будет хорошо).

Другим полезным инструментом являются шаблоны сравнения Boost. Ищите equality_comparable.

1 голос
/ 26 апреля 2010

Другое решение заключается в использовании любопытно повторяющегося шаблона . Это позволяет шаблонному классу определять операторы сравнения на основе производного класса.

Пример:

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)
    { ;}
};

Это можно сделать немного более обобщенно, заменив 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 везде, где требуется сравнение. Я считаю, что это только заставка ввода.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...