Та же функция, разные типы возврата для иерархии классов - PullRequest
0 голосов
/ 11 июня 2009

У нас есть иерархия классов, которая выглядит примерно так:

class base
{
};

class derived1 : protected base
{
private:
    float m_price;
    int m_quantity;
    float m_value;
public:
//  float calculateValue();
};

class derived2 : protected base
{
private:
    double m_price;
    long m_quantity;
    double m_value;
public:
//  double calculateValue();
};

Теперь нам нужно написать функцию, которая вычисляет значение путем умножения цены и количества. Цель состоит в том, чтобы максимально упростить добавление новых классов в будущем. Как вы, возможно, знаете, это не так просто, потому что типы данных этих полей различны для разных классов. По сути, у нас есть эти функции для концептуального выполнения одного и того же, но в терминах программирования это разные операции.

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

template <class A, B, C>
A calculate_value(B price, C quantity)
{
    A result;
    // Some code to do the multiplication, not sure if template specialisation is needed
    return result;
};

class derived1 : protected base
{
private:
    float m_price;
    int m_quantity;
    float m_value;
public:
    float calculateValue()
    {
       calculate_value < float, float, int > (m_price, m_quantity);
    }
};

Он хорошо выполняет свою работу, но это будет означать, что мне нужно определить каждую функцию-член в каждом классе. Например, мне понадобится еще много таких шаблонных функций, если я хочу иметь функцию getValue, скажем.

Типы данных членов класса известны, когда класс определен, поэтому необходимость их повторного включения в определения функций представляется дублированием. Есть ли возможный способ избежать всего этого бизнес-шаблонов в определениях функций?

Спасибо.

Andy

PS Я видел следующий вопрос, но проблема в этом вопросе немного отличается: Возвращает другой тип данных в зависимости от данных (C ++)

Ответы [ 5 ]

6 голосов
/ 11 июня 2009

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

template
class base<typename value_type>
{
public:
    value_type calculateValue();
};
class derived1 : protected base<float>
{
private:
    float m_price;
    int m_quantity;
    float m_value;
};
class derived2 : protected base<double>
{
private:
    double m_price;
    long m_quantity;
    double m_value;
};

Это позволяет вам изменять value_type в производном классе, но объявлять все ваши общие функции в базе (как вы должны это сделать). Это похоже на подход, используемый в STL для карт и тому подобное.

1 голос
/ 11 июня 2009

Используйте шаблон любопытного повторения шаблона (CRTP):

template <typename DERIVED>
class base {
protected:
    typename DERIVED::value_type calculateValue() {
        DERIVED *self = static_cast<DERIVED *>(this);
        return self->m_price * self->m_quantity;
    }
};

class derived1 : protected base<derived1> {
public:
    typedef float value_type;
    float m_price;
    int m_quantity;
};

class derived2 : protected base<derived2> {
public:
    typedef double value_type;
    double m_price;
    long m_quantity;
};

Обратите внимание, что я должен был сделать m_price и m_quantity публичными, чтобы базовый класс мог получить к ним доступ. Вы, вероятно, не хотите этого делать, поэтому вместо этого вам нужно будет либо добавить общедоступные средства доступа (или использовать уже существующие, если они есть), либо сделать их защищенными членами базового класса (типов, указанных в typedefs). в производном классе), или же пусть производный класс объявит базовый класс другом.

Если вам нужна общедоступная getValue функция-член, вы можете добавить ее в базовый класс и сделать наследование общедоступным.

0 голосов
/ 11 июня 2009

Вы можете сделать:

template
class base
{
public:
    void calculateValue(value_type& x);
};
class derived1 : protected base
{
private:
    float m_price;
    int m_quantity;
    float m_value;
};
class derived2 : protected base
{
private:
    double m_price;
    long m_quantity;
    double m_value;
};
0 голосов
/ 11 июня 2009

Хотелось бы что-нибудь подобное сделать?

template <typename A,typename B,typename C>
class base{
protected:
    A m_price;
    B m_quantity;
    C m_value;
public:
    C calculateValue(){
        m_value = m_quantity * m_price;
        return m_value;
    }
};

class derived1 : public base<int,float,int>{
};

class derived2 : public base<long,double,long>{
};
0 голосов
/ 11 июня 2009

OO решение было бы создать класс возвращаемого типа. Затем вы можете создать подкласс этого возвращаемого типа для специализированных возвращаемых типов.

В любом случае, использование математики с плавающей запятой для денег доставит вам неприятности.

...