Полиморфизм (наследование) и типы значений

У меня есть куча типов, PixelMeasure, PointMeasure, CentimeterMeasure и так далее, которые представляют значение с единицей. Я хотел бы, чтобы они

  • семантика значения: например, эффективно неизменяемый, не нужно беспокоиться о распределении памяти, и
  • полиморфизм: я могу вернуть объект типа Measure и может оперировать им, не зная, какой именно это вид. Я также хотел бы иметь возможность поставить несколько разных Measureс в контейнер.

Кажется, они взаимоисключающие в C ++. Для полиморфизма мне нужно использовать указатели или ссылки.

Я вижу два варианта:

  • Используйте умные указатели, например, shared_ptr, Это дает мне поведение, которое я хочу (безопасно, без сырых указателей, но полиморфная отправка). Недостатки:
    • Это многословно (я мог бы скрыть это за typedef, если бы я действительно хотел).
    • У вас под капотом происходит выделение памяти (но код не критичен по производительности и скрыт).
    • Семантика странная — копия моего объекта (shared_ptr<PixelMeasure>) будет использовать тот же базовый указатель. Я все еще могу притворяться, что он имеет семантику значений — если я сделаю интерфейс неизменным, это не должно иметь значения.
  • Я кратко подумал о том, чтобы не использовать наследование (нет общего базового класса) и диспетчеризацию через шаблоны — но в этом случае мне нужно знать точный вид Measure во время компиляции и не могу поместить их в контейнеры.
  • Я мог бы полностью избавиться от классов и использовать только один класс со значением и единичным полем — но это было бы намного менее гибко, а синтаксис использования был бы хуже, поэтому я бы предпочел этого избежать.

Есть идеи?

4

Решение

Вы можете использовать стирание типа, потому что, как выразился Шон Родитель, наследование является базовым классом всего зла. У него также есть презентация Семантика значения и концептуальный полиморфизм что, вероятно, то, что вы хотите. Это та же идея, например, std::function,

По сути, вы используете полиморфизм подтипов через наследование во внутреннем классе, чтобы использовать все, что сопоставляется с концепцией полиморфно. Вот пример из Стирание типа с объединенными понятиями:

class Greeter {
public:
// Constructor: We can stuff anything into a Greeter costume.
template <class T>
Greeter(T data) : self_(std::make_shared<Model<T>>(data)) {}

// External interface: Just forward the call to the wrapped object.
void greet(const std::string &name) const {
self_->greet(name);
}

private:
// The abstract base class is hidden under the covers...
struct Concept {
virtual ~Concept() = default;
virtual void greet(const std::string &) const = 0;
};
// ... and so are the templates.
template <class T>
class Model : public Concept {
public:
Model(T data) : data_(data) {}
virtual void greet(const std::string &name) const override {
// Forward call to user type.
// Requires that T can greet.
data_.greet(name);
}

private:
// The user defined Greeter will be stored here. (by value!)
T data_;
};

// Polymorphic types require dynamic storage.
// Here we store our pointer to the Model that holds the users Greeter.
std::shared_ptr<const Concept> self_;
};

Теперь вы можете поместить все в объект Greeter, у которого есть метод приветствия. Другими примерами являются boost :: any_iterator или std :: function.

Вы будете страдать от одного выделения памяти на одно значение измерения.

7

Другие решения

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

class MeasureWrapper
{
public:
MeasureWrapper(const MeasureWrapper &measureToCopy)
{
m_measure = measureToCopy.m_measure->Clone();
}

MeasureWrapper(Measure *measure) : m_measure(measure)
{
}

~MeasureWrapper()
{
delete m_measure;
}

// Wrap Measure interface here and call m_measure methods...
private:
Measure *m_measure;
};
0

Для этого можно использовать тип варианта: он избегает динамического размещения, но усложняет полиморфную диспетчеризацию.

Увидеть Boost.Variant, и, надеюсь, на горизонте появится стандартная версия.

В качестве альтернативы, вы можете написать более конкретное распознаваемое объединение, предоставляя хороший специфический интерфейс в полиморфном стиле

0
По вопросам рекламы [email protected]