Как сделать вызов члена подкласса из ссылки на базовый класс в полиморфной иерархии - PullRequest
3 голосов
/ 25 ноября 2010

В настоящее время я пишу программу, которая моделирует различные типы чисел и управляет ими полиморфно в объекте Set (уже написано и протестировано).Мои отношения наследования таковы: Multinumber (все виртуальные функции, виртуальный класс), унаследованные от Pairs, Complex, Rational. Все подклассы имеют в основном одни и те же функции с разными параметрами.

Проблема, с которой я сталкиваюсь, заключается в функцияхкак это:

Multinumber& Complex::operator=(const Multinumber &rhs)
{
   imag=rhs.imag;
   real=rhs.real;
   return *this;
}

Поскольку я отношусь к этому полиморфно, все возвращаемые типы и параметры должны быть типа Multinumber, чтобы переопределить параметры базового класса.Тем не менее, я ужасно переживаю компиляцию.Я получаю кучу ошибок по направлениям:

error: 'const class Multinumber' has no member named 'imag'

, что верно.Множественный номер не имеет атрибута или функции imag.Но как я могу заставить компилятор понять, что Multinumber & rhs всегда будет сложным, или рассматривать его как таковой?Спасибо за вашу помощь.

Вот так выглядит мой суперкласс:

class Multinumber
{
public:
virtual Multinumber& operator+(Multinumber&);
virtual Multinumber& operator=(Multinumber&);
virtual bool operator==(Multinumber&);
virtual string object2string();
virtual Multinumber& makeobject(double, double)=0;
};

Ответы [ 5 ]

1 голос
/ 25 ноября 2010

Я думаю, тебе придется сыграть. Попробуйте это:

Multinumber& Complex::operator=(const Multinumber &rhs){
    const Complex & _rhs = dynamic_cast<const Complex &>(rhs);
    imag=_rhs.imag;
    real=_rhs.real;
    return *this;
}
1 голос
/ 26 ноября 2010

Подпись, такая как:

Multinumber& Complex::operator=(const Multinumber &rhs)

означает, что любой тип Multinumber может быть назначен на Complex. Ты действительно хочешь ? У вас есть два варианта здесь:

  • Разрешить и проверить, действительно ли динамический тип параметра Complex (например, через dynamic_cast). Что вы будете делать, если это не так? Вы, вероятно, в конечном итоге бросите исключение.
  • Запретите это в целом, изменив подпись на Multinumber& Complex::operator=(const Complex &rhs): попытка присвоить Rational для Complex не скомпилируется.

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

В дополнение к этому, я думаю, что вы дали ответ, спросив: «Как я могу заставить компилятор понять, что Multinumber & rhs будет всегда будет сложным»: сделайте его Complex и он никогда больше не будет.

EDIT Теперь, когда мы видим, что operator= является виртуальным в Multinumber, кажется, что вы действительно вынуждены придерживаться начальной подписи и проверять динамический тип параметра в Complex::operator= ( см. ответ Стива за это).

0 голосов
/ 26 ноября 2010

Сдайся.Это не может быть сделано.@beldaz прав, что ковариантные аргументы недопустимы в C ++, но упускают реальный смысл: это не поможет, даже если они безопасны от типов.

См. мой ответ в:

C ++ Абстрактный класс не может иметь метод с параметром этого класса

0 голосов
/ 26 ноября 2010

Здесь отсутствует фраза ковариантный тип возврата . В C ++ переопределенному методу разрешено возвращать производную (и, следовательно, ковариантную) ссылку или тип указателя. следовательно Complex& Complex::operator=(const Multinumber &rhs) является допустимым методом переопределения Multinumber& Multinumber::operator=(const Multinumber&)

К сожалению, я думаю, что вы также хотите иметь ковариацию по типу параметра. Это не разрешено стандартом C ++, IFAIK. В этот момент вы, вероятно, должны подумать, действительно ли вы хотите этот полиморфизм. Это означает, что у вас должны быть средства для преобразования между типами. Вы хотите поддерживать преобразование между типами Pair и Complex? Если так, то подход Стива dynamic_cast кажется правильным. В противном случае вы должны удалить такие методы, как operator =, из переопределяемых методов Multinumber и разрешить объявление только значимых методов там, где они принадлежат производным типам.

Обновление В ответ на дальнейшие комментарии: Ковариация типа возврата применяется только к ссылкам и указателям. Возврат по значению не работает, поскольку требования к хранилищу будут отличаться для каждого типа (в отличие от указателей). Таким образом, Multinumber& operator+(Multinumber&) является допустимым методом, который можно переопределить, но он, вероятно, не будет работать так, как ожидалось, поскольку вы не можете вернуть ссылку на вновь созданный тип, поскольку он будет уничтожен после завершения функции. Один из способов обойти это - заменить этот метод на Multinumber& operator+=(const Multinumber&). Для Complex вы можете реализовать его следующим образом:

Complex& Complex::operator+=(const Multinumber &rhs){
    const Complex & _rhs = dynamic_cast<const Complex &>(rhs);
    imag+=_rhs.imag;
    real+=_rhs.real;
    return *this;
}

Альтернативным подходом было бы полностью разобраться с указателями и заставить operator+ вернуть копию new указателя. Но это просто ужасно, и я настоятельно рекомендую вам держаться подальше от таких ужасов - это как C с структурами до появления C ++. Вы могли бы улучшить вещи с помощью некоторой формы полиморфного подхода pimpl (заметьте, я не проверял следующее, это просто для того, чтобы дать вам идею, и это, безусловно, можно улучшить):

class Multinumber
{
public:
  virtual Multinumber* operator+(const Multinumber&);
  virtual Multinumber& operator=(const Multinumber&);
  virtual bool operator==(const Multinumber&) const;
  // etc.
};

class MultinumberOuter
{
  std::unique_ptr<Multinumber> impl_;
public:
  explicit MultinumberOuter(Multinumber* pimpl) : impl_(pimpl) {}

  MultinumberOuter operator+(const MultinumberOuter& src) const {
    return MultinumberOuter(impl_->operator+(*(src.impl_)));
  }

  MultinumberOuter& operator=(const MultinumberOuter& src) {
    impl_->operator=(*(src.impl_));
    return *this;
  }

  bool operator==(const MultinumberOuter& src) const {
    return impl_->operator==(*(src.impl_));
  }
  // etc.
};

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

0 голосов
/ 25 ноября 2010

Если вы УВЕРЕНЫ, что Multinumber всегда сложен, вы можете просто привести свое Rhs к Complex &.

Однако это звучит как неправильный подход.Вместо этого, почему бы вам просто не написать Complex& Complex::operator=(const Complex &rhs)?Ваш оператор не является виртуальным, поэтому нет причин использовать те же типы, что и ваш базовый класс.

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