1

I'm going through a transition from Java to C++ and am trying to write a simple program.

There's a superclass Animal with the following inteface:

class Animal
{
public:
    Animal(int a, std::string n);

    bool operator==(Animal a);
private:
    int age;
    std::string name;
};

And it's subclass Dog:

class Dog : public Animal
{
public:
    Dog(int a, std::string n, double w);

    bool operator==(Dog d);
private:
    double weight;

};

My question is in regards to the Dog's operator== method, which compares 2 dogs.

Animal's operator== is below.

bool Animal::operator==(Animal a) //overriding of operator ==
{
return (age==a.age) && (name==a.name);
}

Now I want to write the Dog's version using Animal's method.

Like I'd do in Java:

boolean equals(Dog d){
    return (super.equals(d)) && (this.name==d.name); 
}

What I need is the c++ equivalent of (super.equals(d)) . If it was a method with a normal name it would be easy(Animal::equals(d)), but I don't know how to do it for operator==, which has a diferent syntax.

3 Answers3

9

It's actually surprisingly easy:

return Animal::operator==(d) && name==d.name;

The reason for using the superclass' name rather than super is that in C++ you can have multiple superclasses, so you have to be clear about which one you want.

Alternatively, you can call it via using it's overload:

return ((Animal&)*this)==d && name==d.name;

Since the paramters to operator== in this case would be an Animal& and a Dog&, then it can't match Dog::operator==(Dog d), and so uses Animal::operator==(Animal a) instead.

Note: Your signatures are highly unusual. Instead, use one of these:

bool operator==(const Animal& a) const;
friend bool operator==(const Animal& left, const Animal& right);

These don't make copies of animals each time you compare, and can compare const animals.

Mooing Duck
  • 64,318
  • 19
  • 100
  • 158
  • thanks, I should have tried adding "operator" like that, but I assumed it wouldnt work because I call the method by doing (object1==object2). – Moonlapse Vertigo Oct 02 '14 at 18:25
0

You can call the operator using verbose notation:

operator==(const Dog& dog) { return Animal::operator==(dog) && weight==dog.weight; }
ArunasR
  • 1,907
  • 1
  • 14
  • 15
  • `weight` is of `double` type, are you sure your code is fine then? – Piotr Skotnicki Oct 02 '14 at 18:25
  • @PiotrS. Why not? It depends on where the actual values come from, but if they are basically immutable, and from human input, then the test is fine. – James Kanze Oct 02 '14 at 18:26
  • @JamesKanze: e.g. to remove this "dependence" you mention about? to allow them to be changed? – Piotr Skotnicki Oct 02 '14 at 18:28
  • @PiotrS. But changed how? I still don't see any reason to avoid the equality here. – James Kanze Oct 02 '14 at 18:32
  • @JamesKanze: [that way](http://coliru.stacked-crooked.com/a/280532a82fad1670), and please don't tell *"but it's fine to compare `15.0==15.0` or `1.0+1.0==2.0` **!**"*, because it's not a big discovery, and it's not an excuse for comparing `doubles` for equality – Piotr Skotnicki Oct 02 '14 at 19:11
  • @PiotrS. What does your example have to do with his code? We're talking about weights here, and you don't multiply two weights. Obviously, he has to take precautions about what he does, but that's true for everything you do with `double`, not just comparison. There are _many_ cases where it is appropriate to compare `double` for equality, and there's nothing in his code that we can see to suggest that this isn't one of them. (Also, you don't suggest an alternative, nor an explication of when it would be appropriate.) – James Kanze Oct 03 '14 at 09:35
  • @JamesKanze: it's not the case *"hey! we don't multiply animal's weight!"*. if you provide a solution for problem titled "how to write equality operator" and you see class' member is double, then the code you write should be universal enough for anyone who will find your answer useful and try to adapt it for own purposes. that's my point, you may not agree with it and I can live with that. you say you don't see in the code that OP multiplies this value, but hey - in the **visible** code OP does not even set this value, then going further with your reasoning - why do you compare it then? – Piotr Skotnicki Oct 03 '14 at 11:12
  • @PiotrS. Weight times weight doesn't make physical sense. And the code I wrote is about as universal as you can get it; there are no universal answers regarding floating point, but given the classes in question, the most reasonable solution to compare the weights is `==`. – James Kanze Oct 03 '14 at 12:46
  • @JamesKanze: note that I told you that *"it's not the case that we don't multiply weights"*, you probably would wrote other solution if the variable was named `double calculated_currency;`, wouldn't you? and this is what I am talking about. The *unviersal* solution is to add a tolerance when comparing doubles, so that one can adjust it to own needs, not providing answer: *"when you write equality operator, use `==` for comparing doubles"*, it's pointless to continue this discussion as this is just about our different views on how the "perfect" answer should look like. – Piotr Skotnicki Oct 03 '14 at 14:18
  • @PiotrS. Adding a tolerance when comparing doubles is _never_ an acceptable policy in an `==` operator. An `==` should define an equivalence relationship; adding a tolerance will break this. In fact, it's actually rare that adding a tolerance is the correct solution when comparing doubles. – James Kanze Oct 03 '14 at 15:02
  • @JamesKanze: I doubt two objects `a1` and `a2` defined as `a1.setValue(30.0+0.3)` and `a2.setValue(3.0*10.1)` should *never ever* (like you say) be equal – Piotr Skotnicki Oct 04 '14 at 18:06
  • @PiotrS. You're not addressing the issue. The issue is where the values come from (expressions consisting entirely of floating point literals aren't realistic). And the meaning of `==`. There is no universal answer, but allowing a tolerance in an implementation of `==` is definitely incorrect. – James Kanze Oct 06 '14 at 08:52
0

The direct equivalent of your Java equals would be:

bool Dog::operator==(Dog d)
{
    return Animal::operator==(d) && weight == d.weight;
}

But I'm not sure if this is what you really want. For starters, you're taking the argument by copy, which means that your comparing a copy. In particular, when you call Animal::operator==, you will pass a copy of the Animal part of Dog, not the complete object. Class types are usually passed by reference; reference to const if you don't want to modify them. So the signature in the base would be something like:

bool Animal::operator==( Animal const& other ) const
{
    //  ...
}

And similarly in Dog. Also, the comparison operator in Dog would probably take an Animal const&, not a Dog const&. (In Java, equals takes a java.lang.Object, always.) Which means that you'd have to verify that it was a Dog:

bool Dog::operator==( Animal const& other ) const
{
    return dynamic_cast<Dog const*>( &other ) != nullptr
        && Animal::operator==( other )
        && weight == other.weight;
}

EDIT:

As was pointed out in a comment, while this addresses the immediate syntax issue raised by the original poster, it isn't really the way we'd do this normally. The usual solution would be something like:

class Animal
{
    //    If Animal is actually an abstract class (usually the case
    //    in real code), this would be a pure virtual.
    //    Derived classes overriding this function are guaranteed
    //    that other is actually of the same type as they are,
    //    so they can just static_cast it to their type.
    virtual bool doIsEqual( Animal const& other ) const
    {
        return true;
    }
public:
    bool isEqual( Animal const& other )
    {
        return typeid( *this ) == typeid( other )
            && //  ... his local conditions...
            && doIsEqual( other );
    }
};

bool operator==( Animal const& lhs, Animal const& rhs )
{
    return lhs.isEqual( rhs );
}

bool operator!=( Animal const& lhs, Animal const& rhs )
{
    return !lhs.isEqual( rhs );
}

The implementation of operator== and operator!= can in fact be done by inheriting from an appropriate class template, which avoids some of the boilerplate if you have a lot of classes which must support == and the others.

James Kanze
  • 150,581
  • 18
  • 184
  • 329
  • If you have subclasses of a *concrete* class, your method of comparing things requires some adjustment. Try `Animal a(1, "Chopsticks"); Dog b(1, "Chopsticks", 30); (a==b) == (b==a)`. – n. m. could be an AI Oct 02 '14 at 18:43
  • @n.m. Yes. In practice, I'd use a virtual `isEqual` function in `Animal`, with free functions `operator==` and `operator!=` which call it, for `Animal` only. (But I'm a bit doubtful of subclassing concrete classes anyway, unless I'm using the template method pattern. I think that the OP really just created an artificial example to demonstrate the syntax question he was asking; this sort of hierarchy doesn't occur in well written code.) – James Kanze Oct 03 '14 at 09:23
  • Comparing typeids has its own pitfalls. Suppose someone releases a geometry library with various shape classes, and someone else extends it to a drawing library, adding colour and other drawing styles. Now coloured-shape is never equal to a regular uncoloured shape. But the geometry library doesn't care about colours, it needs geometric equality. Also differently coloured shapes are always unequal, which is OK for the drawing operations but breaks geometry operations that rely on geometric comparison. TL;DR `==` is dangerous in hierarchies however you look at it. – n. m. could be an AI Oct 03 '14 at 09:43
  • @n.m Defining an equivalence relationship over polymorphic types has a number of pitfalls. The simplest solution is to consider the type part of the value, so that different types always compare unequal. For specific cases, you might want to define it differently. Which can make things more complicated; you'd probably have to forward to the derived class in every case, and let it decide. (Most of the times I've dealt with polymorphic objects, they had identity, and comparing their addresses was most appropriate.) – James Kanze Oct 03 '14 at 11:00