В C ++ 11 вы можете легко избежать наследования:
struct Interface {
explicit Interface(SomeType& other)
: foo([=](){ return other.my_foo(); }),
bar([=](){ return other.my_bar(); }), /*...*/ {}
explicit Interface(SomeOtherType& other)
: foo([=](){ return other.some_foo(); }),
bar([=](){ return other.some_bar(); }), /*...*/ {}
// you can add more types here...
// or use a generic constructor:
template<class T>
explicit Interface(T& other)
: foo([=](){ return other.foo(); }),
bar([=](){ return other.bar(); }), /*...*/ {}
const std::function<void(std::string)> foo;
const std::function<void(std::string)> bar;
// ...
};
В этом случае интерфейс имеет ссылочную семантику, т.е. вы должны убедиться, что объект переживает интерфейс (также возможно создание интерфейсов с семантикой значения).
Интерфейсы такого типа имеют свои плюсы и минусы:
- Они требуют больше памяти , чем полиморфизм на основе наследования.
- Они , как правило, быстрее , чем полиморфизм, основанный на наследовании.
- В тех случаях, когда вы знаете конечный тип, они намного быстрее! (некоторые компиляторы, такие как gcc и clang, проводят больше оптимизаций в типах, которые не имеют / наследуют от типов с виртуальными функциями).
Наконец, наследование является корнем всего зла в разработке сложных программ. В Семантике значения Шона Родителя и основанном на понятиях полиморфизме (настоятельно рекомендуется, там объясняются лучшие версии этого метода) изучается следующий случай:
Допустим, у меня есть приложение, в котором я полиморфно работаю с формами, используя интерфейс MyShape
:
struct MyShape { virtual void my_draw() = 0; };
struct Circle : MyShape { void my_draw() { /* ... */ } };
// more shapes: e.g. triangle
В вашем приложении вы делаете то же самое с различными формами, используя интерфейс YourShape
:
struct YourShape { virtual void your_draw() = 0; };
struct Square : YourShape { void your_draw() { /* ... */ } };
/// some more shapes here...
Теперь скажите, что вы хотите использовать некоторые формы, которые я разработал в вашем приложении. Концептуально, наши фигуры имеют тот же интерфейс, но чтобы мои фигуры работали в вашем приложении, вам нужно расширить мои фигуры следующим образом:
struct Circle : MyShape, YourShape {
void my_draw() { /*stays the same*/ };
void your_draw() { my_draw(); }
};
Во-первых, изменение моих форм может оказаться невозможным вообще. Кроме того, множественное наследование ведет к созданию спагетти-кода (представьте, что третий проект, использующий интерфейс TheirShape
... что произойдет, если они также вызовут свою функцию рисования my_draw
?).
Обновление: есть пара новых ссылок о полиморфизме, не основанном на наследовании: