Могу ли я реализовать переопределение виртуальной функции, которая получает в качестве параметра класс отца?
Я пытаюсь переопределить 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);
Итак, вопросы:
как рассчитать
ab
?ОК, это легко:
point
б являетсяobject
так мы называемa->object::equal(object const&)
,как рассчитать
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
, Код не очень хорошо масштабируется по мере роста числа подклассов, но его намного проще реализовать для небольших случаев, чем полноценного посетителя.