У меня есть куча типов, PixelMeasure
, PointMeasure
, CentimeterMeasure
и так далее, которые представляют значение с единицей. Я хотел бы, чтобы они
Measure
и может оперировать им, не зная, какой именно это вид. Я также хотел бы иметь возможность поставить несколько разных Measure
с в контейнер.Кажется, они взаимоисключающие в C ++. Для полиморфизма мне нужно использовать указатели или ссылки.
Я вижу два варианта:
shared_ptr
, Это дает мне поведение, которое я хочу (безопасно, без сырых указателей, но полиморфная отправка). Недостатки:
shared_ptr<PixelMeasure>
) будет использовать тот же базовый указатель. Я все еще могу притворяться, что он имеет семантику значений — если я сделаю интерфейс неизменным, это не должно иметь значения.Есть идеи?
Вы можете использовать стирание типа, потому что, как выразился Шон Родитель, наследование является базовым классом всего зла. У него также есть презентация Семантика значения и концептуальный полиморфизм что, вероятно, то, что вы хотите. Это та же идея, например, 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.
Вы будете страдать от одного выделения памяти на одно значение измерения.
Вы можете использовать класс-оболочку с соответствующим копирующим конструктором и указателем на свой показатель как поле. Вам, вероятно, понадобится добавить метод клонирования в 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;
};
Для этого можно использовать тип варианта: он избегает динамического размещения, но усложняет полиморфную диспетчеризацию.
Увидеть Boost.Variant, и, надеюсь, на горизонте появится стандартная версия.
В качестве альтернативы, вы можете написать более конкретное распознаваемое объединение, предоставляя хороший специфический интерфейс в полиморфном стиле