Guy Sadoun
Guy Sadoun

Reputation: 535

Can I implement an override of a virtual function which gets as parameter a father class?

I am trying to override the equal method in the following code:

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);
  }
};

I thought of overloading operator== and use it as a friend function but it would help me if I could do it somehow in this case...

Upvotes: 0

Views: 237

Answers (3)

Useless
Useless

Reputation: 67812

There are a couple of senses in which this is problematic.

Firstly, your existing code definitely won't work, and if you had tried the compiler would have told you so.

Let's examine why

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

so, questions:

  1. how should we calculate ab?

    OK, that one's easy: the point b is an object, so we call a->object::equal(object const&).

  2. how should we calculate ba?

    This one's harder.

    We can use b->object::equal(object const &), which has the benefit of being symmetric (it's usually nicer if a.equal(b) == b.equal(a).

    Or we could write an override, so we call b->point::equal(object const &), and have it decide whether its argument object is really a point. (This is Holt's dynamic_cast solution).

For the last case, we could have an overload (instead of an override) so point has two equal methods, one taking an object (this can really be the base-class method) and one taking a point. However, this only works for static type.

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
};

Now we have an overload rather than an override (so there's no point using virtual at all at this stage), we can write

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&)

However, as soon as we start storing base-class pointers or references (which is the normal reason for using virtual in the first place), we lose the dynamic type information:

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

If you need this case to use the dynamic type (ie, figure out that r really refers to a point), you need to use something like dynamic_cast. The code doesn't scale very nicely as the number of subclasses grows, but it's a lot simpler to implement for small cases than a full-blown Visitor.

Upvotes: 1

Eljay
Eljay

Reputation: 5321

If you change the signature of the equal method, it hides the parent rather than overrides it. You could do something like this (room for efficiency improvement):

#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;
}

Upvotes: 1

Holt
Holt

Reputation: 37661

No you cannot, try adding override:

bool equal(const point& o) const override;

...and you get a compilation error:

error: 'bool point::equal(const point&) const' marked 'override', but does not override

This is normal design for an object-oriented language, imagine the following:

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

p->equal(p2);

p->equal would find point::equal(point const&) by virtual lookup but p2 is not a point, it is an object, so what the compiler should do?

If you want to override equal, you could do the following:

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;
}

...but maybe you should rethink your design instead of doing this?

Upvotes: 2

Related Questions