Переопределение typedef в производном классе? - PullRequest
7 голосов
/ 13 сентября 2011

Итак, после долгих поисков ответа на мой вопрос я, наконец, разочаровался в своих навыках Google.

У меня есть базовый класс Base и производный класс Производный . Я хочу переопределить тип в классе Base с типом в классе Derived . Вот пример:

class Apple {
public:
    Apple() { }

    // ...

};

class Orange {
public:
    Orange() { }

    // ...

};

class Base {
public:
    typedef Apple fruit;

    // ...

    virtual fruit func() { return Apple(); }
};

class Derived : public Base {
public:
    typedef Orange fruit;

    // ...

    fruit func() override { return Orange(); } // <-- Error C2555!
};

Этот код не работает, и он дает

C2555 error ('Derived::func': overriding virtual function return type differs and is not covariant from 'Base::func').

Выше было одно из решений, которое я попробовал. Я также попытался создать виртуальный вложенный класс в Base и переопределить в Derived , но это также не компилировалось (это также было очень грязно).

Я также не могу извлечь яблоки и апельсины из одного базового класса, чтобы возвращать указатель / ссылку на их родительский класс в Base и Derived . Мне нужно физически вернуть экземпляр объекта.

  1. Есть ли способ объявить абстрактные typedefs?
  2. Если нет, есть ли другое решение, которое могло бы достичь того, что пытаешься сделать?

Ответы [ 4 ]

7 голосов
/ 13 сентября 2011

Прежде всего, посмотрите на этот синтаксис:

fruit func() override { return Orange(); }

Что такое override? В C ++ 03 такого ключевого слова нет. Это только в C ++ 11. Поэтому убедитесь, что вы используете компилятор, который знает об этом ключевом слове.

Во-вторых, в производном классе fruit действительно Orange. Переопределение typedef не является проблемой. Проблема в том, что Orange и Apple не являются ковариантными типами. Производные друг от друга сделают их ковариантными. В вашем случае вы должны извлечь Orange из Apple, чтобы заставить его работать.

Обратите внимание, что вы должны изменить тип возврата с fruit на fruit* или fruit&.

class Orange : public Apple {}; //correct - your code will work

class Apple : public Orange {}; //incorrect - your code will not work

Идея состоит в том, что в базовом классе тип возвращаемого значения должен быть указатель / ссылка базового типа (который равен Apple), а в производном классе тип возвращаемого значения может быть указатель / ссылка типа Apple или любого класса, производного от него.

Кстати, это имеет смысл? Вывести Orange из Apple?

Как насчет следующего класс-дизайна?

class Fruit {};
class Apple : public Fruit {};
class Orange : public Fruit {};

class Base
{
   virtual Fruit* f();
};

class Derived : public Base 
{
   virtual Fruit* f();
};

Не нужно использовать typedef.

1 голос
/ 13 сентября 2011

Это не имеет большого смысла для начала. Derived должен быть в состоянии использоваться везде, где ожидается Base, поэтому вы должны иметь возможность

Base *foo = new Base();
Apple x = foo->func();   // this is fine

Base *bar = new Derived();
Apple y = foo->func();   // oops...

Я думаю, вам нужно изучить другой дизайн. Не совсем ясно, какова ваша цель здесь, но я предполагаю, что вы, возможно, захотите, чтобы Base был шаблоном класса с Fruit в качестве параметра шаблона, или, возможно, вам нужно полностью избавиться от наследования.

0 голосов
/ 13 сентября 2011

Об ошибке сообщает вам все, что вам нужно. Это означает, что тип, возвращаемый в методе Derived, должен быть получен из типа, возвращенного в базовом методе.

Это так, что если базовый метод вызывается виртуально, возвращенный объект может быть просмотрен как возвращенный базовым методом.

На самом деле, вам нужно вернуть указатель или ссылку, чтобы сделать это.

Вам нужно будет определить новый базовый класс Fruit и получить из него Apple и Orange.

Тогда пусть func() вернет Fruit*.

Это поставит перед вами задачу убедиться, что Fruit* равен delete d в какой-то момент.

С немного большим контекстом, я подозреваю, что (вероятно, тонкий) шаблонирование - это решение, которое вы ищете, а не наследование.

0 голосов
/ 13 сентября 2011

Вы не можете этого сделать, если у вас есть объект значения, вы должны знать, какой это тип. (Это одно из основных различий между языком со статической типизацией, таким как C ++, и языками с динамической типизацией, такими как Ruby и Python.)

Есть много способов обойти это. Во-первых, давайте предположим, что Apple и Oragne имеют общий базовый класс с именем Fruit. Одним из решений является динамическое выделение объекта с использованием new и возвращение Fruit -показателя.

Другое решение, вместо того, чтобы возвращать значение, ваша функция могла бы взять указатель или ссылку на Fruit, который она затем могла бы заполнить.

Еще одно решение состоит в том, чтобы иметь некоторый контейнерный объект, который бы внутри содержал либо Apple, либо Orange. Таким образом, вы можете вернуть его.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...