У меня есть иерархия классов с несколькими уровнями наследования.
cloneable
объявляет чисто виртуальную функцию-член, возвращающую cloneable *
,base
происходит от cloneable
, но не объявляет никаких функций-членов. derived
происходит от base
и определяет виртуальную функцию, но переопределяет тип возвращаемого значения derived *
,Вызов виртуальной функции через base
указатель на derived
возврат объекта cloneable *
, я ожидал base *
потому что реализация виртуальной функции возвращает derived *
который конвертируется в base *
, Что здесь происходит?
Если я объявлю чистую виртуальную функцию в base
Наконец могу получить base *
от этого, но я не понимаю, зачем нужна эта декларация.
Код:
struct cloneable
{
virtual cloneable * clone() = 0;
};
struct base : cloneable
{
// virtual base * clone() = 0; // this line resolves the compile error
};
struct derived : base
{
virtual derived * clone () { return new derived; }
};
int main(int, char**)
{
derived d;
base * bp = &d;
base * bbp = bp->clone(); // error: invalid conversion
// from ‘cloneable*’ to ‘base*’
return 0;
}
Примечание. Я намеренно опустил виртуальный деструктор, чтобы сократить пример кода.
Как вы думаете, как именно компилятор должен догадаться, что вам нужна версия, возвращающая base*
без каких-либо деклараций?
Хотя вышеприведенный вопрос отвечает на ваш прямой вопрос, я чувствую, что должен также добавить несколько советов.
Прежде всего,
clone
функция const
так что его можно вызвать на const
объект или через выражение rvalue.т.е.,
virtual cloneable* clone() const;
Во-вторых, чтобы создать клон объекта,
new T( *this )
(используя конструктор копирования), не new T
(используя конструктор по умолчанию).И в-третьих,
clone
операция возвращает умный указатель, такой как unique_ptr<MyClass>
, а не необработанный указатель.Однако с изменением типа возвращаемого значения на интеллектуальный указатель вы больше не получите прямой выгоды от поддержки C ++ для результаты ковариантной функции, который только для сырых указателей и ссылок. Так что один из способов сделать это — этоpublic
реализация результата необработанного указателя, которая может иметь ковариантный тип результата и просто типизированный public
Обертка, которая возвращает умный указатель. По сути, вы сами реализуете ковариацию для открытого интерфейса, и это может выглядеть так:
#include <memory> // std::unique_ptr
using namespace std;
class Base
{
private:
virtual Base* virtualClone() const
{
return new Base( *this );
}
public:
unique_ptr< Base > clone() const
{
return unique_ptr< Base >( virtualClone() );
}
};
class Derived
: public Base
{
private:
virtual Derived* virtualClone() const
{
return new Derived( *this );
}
public:
unique_ptr< Derived > clone() const
{
return unique_ptr< Derived >( virtualClone() );
}
};
int main()
{
Derived d;
Base* bp = &d;
unique_ptr< Base > bbp = bp->clone();
}
Других решений пока нет …