Могу ли я реализовать переопределение виртуальной функции, которая получает в качестве параметра класс отца?

Я пытаюсь переопределить equal метод в следующем коде:

class object{
  int a;
public:
  virtual bool equal(const object& o) const{
    return this->a==o.a; // not sure if i can access private field (o.a) from one object function to the object o so it's another question
  }
};

class point: public object{
  double x;
  double y;
public:
  bool equal(const point& o) const override{
    return (this->x==o.x && this->y==o.y);
  }
};

Я думал о перегрузке operator== и использовать его как friend функция, но это помогло бы мне, если бы я мог сделать это как-то в этом случае...

3 ответа

Нет, вы не можете, попробуйте добавить override:

bool equal(const point& o) const override;

... и вы получите ошибку компиляции:

ошибка: 'bool point::equal(const point&) const' помечена как 'override', но не переопределяет

Это нормальный дизайн для объектно-ориентированного языка, представьте следующее:

object *p = new point();
object p2;

p->equal(p2);

p->equal найдет point::equal(point const&) с помощью виртуального поиска, но p2 это не point, это objectтак что должен делать компилятор?

Если вы хотите переопределить equalВы могли бы сделать следующее:

bool equal(const object& o) const override {
    auto *p = dynamic_cast<const point*>(&o);
    if (!p) {
        return false;
    }
    return this->x == p->x && this->y == p->y;
}

... но, может быть, вы должны переосмыслить свой дизайн вместо того, чтобы делать это?

Если вы изменяете сигнатуру равного метода, он скрывает родительский элемент, а не переопределяет его. Вы могли бы сделать что-то вроде этого (пространство для повышения эффективности):

#include <iostream>
#include <iomanip>

using std::cout;
using std::endl;
using std::boolalpha;

class object{
  int a;
public:
  object(int value) : a{value} {}
  virtual bool equal(object const& o) const {
    return typeid(*this) == typeid(o)
      && this->a == o.a;
  }
};

class point : public object {
  double x;
  double y;
public:
  point(int value, double xval, double yval) : object{value}, x{xval}, y{yval} {}
  bool equal(object const& o) const {
    return object::equal(o)
      && this->x == dynamic_cast<point const&>(o).x
      && this->y == dynamic_cast<point const&>(o).y;
  }
};

int main() {
  auto o1 = object{7};
  auto o2 = object{7};
  auto p3 = point{7, 8.0, 9.0};
  auto p4 = point{7, 8.0, 9.0};
  auto p5 = point{7, 8.1, 9.1};

  cout << boolalpha;
  cout << "o1 equal o2? " << o1.equal(o2) << "\n";
  cout << "o1 equal p3? " << o1.equal(p3) << "\n";
  cout << "p3 equal p4? " << p3.equal(p4) << "\n";
  cout << "p3 equal p5? " << p3.equal(p5) << "\n";
  cout << "o2 equal o1? " << o2.equal(o1) << "\n";
  cout << "p3 equal o1? " << p3.equal(o1) << "\n";
  cout << "p4 equal p3? " << p4.equal(p3) << "\n";
  cout << "p5 equal p3? " << p5.equal(p3) << "\n";
  cout << "---done---" << endl;
}

Есть пара чувств, в которых это проблематично.

Во-первых, ваш существующий код определенно не будет работать, и если бы вы попробовали, компилятор сказал бы вам об этом.

Давайте посмотрим, почему

object a{42};
point b{1.2, 3.4};
bool ab = a.equal(b);
bool ba = b.equal(a);

Итак, вопросы:

  1. как рассчитать ab?

    ОК, это легко: point б является objectтак мы называем a->object::equal(object const&),

  2. как рассчитать ba?

    Это сложнее.

    Мы можем использовать b->object::equal(object const &), который имеет преимущество в том, чтобы быть симметричным (обычно лучше, если a.equal(b) == b.equal(a),

    Или мы могли бы написать переопределение, поэтому мы называем b->point::equal(object const &)и пусть он решает, является ли его объект аргумента действительно point, (Это Холт dynamic_cast решение).

В последнем случае мы могли бы иметь перегрузку (вместо переопределения), так point имеет два equal методы, один принимая object (это действительно может быть метод базового класса) и один принимает point, Однако это работает только для статического типа.

class point : public object{
  double x;
  double y;
public:
  bool equal(const point& o) const {
    return (this->x==o.x && this->y==o.y);
  }
  using object::equal; // stop this being hidden
};

Теперь у нас есть перегрузка, а не переопределение (поэтому нет смысла использовать virtual вообще на данном этапе) мы можем написать

point c{5.6, 7.8};
bool ba = b.equal(a); // uses b->object::equal(object const&)
bool bc = b.equal(c); // uses b->point::equal(point const&)

Однако, как только мы начинаем хранить указатели или ссылки базового класса (что является нормальной причиной для использования virtual во-первых, мы теряем информацию о динамическом типе:

object &r = c;
bool br = b.equal(r); // uses object::equal(object const&)

Если вам нужен этот случай, чтобы использовать динамический тип (т.е. выяснить, что r действительно относится к point), вам нужно использовать что-то вроде dynamic_cast, Код не очень хорошо масштабируется по мере роста числа подклассов, но его намного проще реализовать для небольших случаев, чем полноценного посетителя.

Другие вопросы по тегам