Свободные интерфейсы и наследование в C++
Я хотел бы построить базовый (абстрактный) класс (назовем его type::base
) с некоторой общей функциональностью и плавным интерфейсом, проблема, с которой я сталкиваюсь, - это возвращаемый тип всех этих методов
class base {
public:
base();
virtual ~base();
base& with_foo();
base& with_bar();
protected:
// whatever...
};
Теперь я мог бы сделать подтипы, например:
class my_type : public base {
public:
myType();
// more methods...
};
Проблема возникает при использовании таких подтипов, как:
my_type build_my_type()
{
return my_type().with_foo().with_bar();
}
Это не будет компилироваться, потому что мы возвращаем base вместо my_type.
Я знаю, что мог бы просто:
my_type build_my_type()
{
my_type ret;
ret.with_foo().with_bar();
return ret;
}
Но я думал, как я могу это реализовать, и я не нашел никаких обоснованных идей, каких-то предложений?
5 ответов:
Эту проблему "потери типа" можно решить с помощью шаблонов-но это довольно сложно.
Напр.
class Pizza { string topping; public: virtual double price() const; }; template <class T, class Base> class FluentPizza : public Base { T* withAnchovies() { ... some implementation ... }; }; class RectPizza : public FluentPizza<RectPizza, Pizza> { double price() const { return length*width; :) } }; class SquarePizza : public FluentPizza<SquarePizza, RectPizza> { ... something else ... };
Затем вы можете написать
SquarePizza* p=(new SquarePizza)->withAnchovies();
Картина такова, что вместо
class T : public B
Вы пишете
class T : public Fluent<T, B>
Другой подход может заключаться не в использовании интерфейса fluent на объектах, а на указателях:
class Pizza { ... }; class RectPizza { ... }; class SquarePizza { ... whatever you might imagine ... }; template <class T> class FluentPizzaPtr { T* pizza; public: FluentPizzaPtr withAnchovies() { pizza->addAnchovies(); // a nonfluent method return *this; } };
Используйте вот так:
FluentPizzaPtr<SquarePizza> squarePizzaFactory() { ... } FluentPizzaPtr<SquarePizza> myPizza=squarePizzaFactory().withAnchovies();
Вы должны возвращать ссылки / указатели, и вам не нужно хранить информацию о типе.
class base { public: base(); virtual ~base(); base &with_foo(); base &with_bar(); protected: // whatever... }; class my_type : public base { public: my_type(); // more methods... }; base *build_my_type() { return &new my_type()->with_foo().with_bar(); }
У вас уже есть виртуальный деструктор. Вероятно, у вас есть и другие виртуальные функции. Доступ ко всему через базовый тип и виртуальные функции, объявленные там.
Одно решение будет работать следующим образом:
return *static_cast<my_type*>(&my_type().with_foo().with_bar());
Использование
static_cast
в основном говорит компилятору: "я знаю, что я здесь делаю".
В языке C++ вы должны ретурировать указатели или ссылки, а не значения. Кроме того, вы можете объяснить, что вы подразумеваете под "плавными интерфейсами".
Способ, которым я сделал бы это в C#, и я думаю, что он будет работать и в C++, - это предоставить реализацию по умолчанию для
with_foo()
иwith_bar()
... Простите мой c#, но:class base { virtual base with_foo() { throw new NotImplementedException(); } virtual base with_bar(); { throw new NotImplementedException(); } }