Тот факт, что вы используете свои знания Java, чтобы рассуждать о C ++, доставит вам немало неприятностей, поскольку на самом деле это совершенно разные языки.
На этот раз вы столкнулись с проблемой, потому что Java по умолчанию использует ссылочную семантику, а C ++ использует семантику значений. Возврат по значению из функции означает (кроме случаев исключения, которые я не буду расширять) создание экземпляра возвращаемого типа, что невозможно для абстрактного класса. Возврат по значению также вызывает нарезку объекта, которую вы видите, когда делаете базу не абстрактной.
Чтобы исправить проблему в C ++, необходимо использовать указатель в какой-либо форме - предпочтительно интеллектуальный указатель. Я предоставлю вариант с использованием шаблонного класса std::unique_ptr
(в стандартном заголовке <memory>
). Это работает только для C ++ 11 и более поздних версий (поскольку unique_ptr
был введен в C ++ 11).
Во-первых, измените тип возвращаемого значения inverse()
template<typename T>
class KinematicChainSegment
{
public:
// other member functions omitted
virtual std::unique_ptr<KinematicChainSegment<T> > inverse() const = 0;
};
затем переопределите его в производном классе
template<typename T>
class StaticLink : public KinematicChainSegment<T>
{
public:
virtual std::unique_ptr<KinematicChainSegment<T> > inverse() const override;
protected:
Eigen::Matrix<T,4,4> _transformationMatrix;
};
Обратите внимание, что возвращаемый тип переопределенного inverse()
остается таким же, как и в базовом классе. Изменение типа возвращаемого значения на std::unique_ptr<StaticLink<T> >
не сработает - хотя StaticLink<T>
является производным от KinematicChainSegment<T>
, класс std::unique_ptr<StaticLink<T> >
фактически не является производным от std::unique_ptr<KinematicChainSegment<T> >
.
Хотя это определение (реализация) встроенная функция возможна, я определю функцию вне строки для простоты объяснения.
template<class T>
std::unique_ptr<KinematicChainSegment<T> > StaticLink<T>::inverse() const
{
return std::make_unique<StaticLink<T> >(_transformationMatrix.inverse());
}
Этот вызов std::make_unique()
создает эффект std::unique_ptr<StaticLink<T> >
, который управляет объектом, созданным из _transformationMatrix.inverse()
.
Это работает, поскольку std::unique_ptr
поддерживает неявные преобразования. StaticLink<T>
наследуется от KinematicChainSegment<T>
, поэтому преобразование std::unique_ptr<StaticLink<T> >
в std::unique_ptr<KinematicChainSegment<T> >
допустимо - несмотря на то, что между ними нет отношения наследования.
Вышеупомянутое означает, что вызывающий получит KinematicChainSegment<T>
, а затем может полиморфно использовать управляемый. Например;
int main()
{
StaticLink<T> static_link;
// presumably set state of the object static_link here
std::unique_ptr<KinematicChainSegment<T> > clone = static_link.inverse();
// Use virtual functions of clone
std::unique_ptr<KinematicChainSegment<T> > clone_inverse = clone->inverse();
}
В этом случае clone
- это std::unique_ptr<KinematicChainSegment<T> >
, который управляет указателем на StaticLink<T>
. Таким образом, вызов clone->inverse()
является polymorphi c, а также возвращает std::unique_ptr<KinematicChainSegment<T> >
, который управляет указателем на StaticLink<T>
.
Если вы правильно спроектируете (как и в случае любой формы наследования, независимо от того, с использованием шаблонных классов или нет) main()
вообще не обязательно иметь жестко запрограммированные знания о классе StaticLink
, поскольку вызовы функций-членов будут полиморфными c (разрешить тип фактического содержащегося объекта).
Также можно использовать сырые (не интеллектуальные указатели). У этого есть множество недостатков, поэтому я не буду это демонстрировать.
Вышесказанное (я полагаю) сложнее, чем вы ожидали, когда смешиваете использование шаблонов с наследованием. Я настоятельно рекомендую не смешивать шаблоны с наследованием, пока вы не узнаете значительно больше о каждом (независимо).
Также, возвращаясь к моему открытому комментарию. Если вы хотите изучать C ++, не пытайтесь сопоставить свои знания Java с C ++. Java и C ++ на самом деле являются совершенно разными языками, даже если их синтаксис похож, и способ их работы совершенно другой. На самом деле вам нужно будет отучиться от многих техник, которые популярны в Java, потому что они просто не будут работать так, как вы ожидаете в C ++. (Точно так же тот, кто изучает Java на основе знания C ++, столкнется с проблемами, потому что многие методы не работают).