Виртуальная функция не возвращает производный тип с многоуровневым наследованием

У меня есть иерархия классов с несколькими уровнями наследования.

  1. cloneable объявляет чисто виртуальную функцию-член, возвращающую cloneable *,
  2. base происходит от cloneable, но не объявляет никаких функций-членов.
  3. В заключение, 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;
}

Примечание. Я намеренно опустил виртуальный деструктор, чтобы сократить пример кода.

2

Решение

Как вы думаете, как именно компилятор должен догадаться, что вам нужна версия, возвращающая 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();
}
4

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]