Calling superclass method operator== [duplicate] - c++

This question already has answers here:
How to call a parent class function from derived class function?
(7 answers)
Closed 8 years ago.
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.

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.

You can call the operator using verbose notation:
operator==(const Dog& dog) { return Animal::operator==(dog) && weight==dog.weight; }

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.

Related

It is possible to assign an object to another with a different type

I'm working with a C++ project and I need to do some assignment code to assign one object to another with a different type like this:
MyClass1 o1;
MyClass2 o2;
o2 = o1;
Ofc, we can make this work with the help of a copy assignment operator of MyClass2: MyClass2& operator=(const MyClass1&).
But this gonna be a very heavy job for me because there has been thousands of classes, which need to do the assignment like o2 = o1. I don't want to add a copy assignment operator for each of them one by one...
I'm thinking if there is some other way, such as some TMP method to help me...
I can ensure that MyClass1 and MyClass2 have exactly the same data members with the same declaration order (see below). If so, is there some TMP method, which could help me?
struct MyClass1 {
int a;
char ch;
std::string msg;
// some virtual member functions
};
struct MyClass2 {
int a;
char ch;
std::string msg;
// some virtual member functions
};
BTW, you may want to ask why there are such two classes/structs with the same data members. Well, this is about some historical reason, I can't fusion them onto one class/struct.
UPDATE
It seems that I didn't make my question clear. I'll make an example here.
void doJob(const MyClass1& o1) {}
void func1(MyClass1 o1) {
doJob(o1);
}
void func2(MyClass2 o2) {
MyClass o1;
o1.? = o2.?; // assign each element of o2 to o1.
doJob(o1);
}
Here is the real case. As you see, o1.? = o2.? contains multi lines, it depends on how many data members of MyClass1/MyClass2. I'm trying to find some way to avoid this stupid assignment of all data members one by one in the func2.
Also, as I said, I have thousands of classes like MyClass1/MyClass2, meaning that these classes have totally different data members.
So for MyClass1 and MyClass2, o1.? = o2.? is o1.a = o2.a; o1.ch = o2.ch; o1.msg = o2.msg; But for other classes, it may become o1.f = o2.f; o1.vec = o2.vec;. That's why I'm thinking I may need some TMP technique...
UPDATE2
Alice developed the classes:
struct MyClass1 {// data members};
struct MyClass2 {// data members};
// MyClass1 and MyClass2 have exactly the same data members and declaration order
struct MyClass3 {// data members};
struct MyClass4 {// data members};
// MyClass3 and MyClass4 have exactly the same data members and declaration order
...
...
struct MyClass1000 {// data members};
struct MyClass1001 {// data members};
// MyClass1000 and MyClass1001 have exactly the same data members and declaration order
I'm developing the functions:
void doJob1(const MyClass1& o1) {}
void func1(MyClass1 o1) {
doJob(o1);
}
void func2(MyClass2 o2) {
MyClass1 o1;
o1.? = o2.?; // assign each element of o2 to o1.
doJob1(o1);
}
...
...
void doJob1000(const MyClass1000& o1) {}
void func1000(MyClass1000 o1) {
doJob1000(o1);
}
void func1001(MyClass1001 o2) {
MyClass1000 o1;
o1.? = o2.?; // assign each element of o2 to o1.
doJob1000(o1);
}
Why did Alice do such a stupid design? For some historical reason...
Why not using std::memcpy? Because these classes contain virtual functions.
yes, you can... but I is not recommended unless the two classes have an exact one to one correspondence and the exact same meaning in the domain of your problem.
Why is not recommended?
Because the operation needs to be manually implemented (the compiler cannot help with a = default declaration).
auto operator=(MyClass1 const& other) -> MyClass2& {
std::tie(a, ch, msg) = std::tie(other.a, other.ch, other.msg);
return *this;
}
Because if you defined assignment, you will eventually need to define equality (==), and inequality (!=) and in both directions. Otherwise the clases will not behave logically.
bool operator==(MyClass1 const& mc1, MyClass2 const& mc2) {
return std::tie(mc1.a, mc1.ch, mc1.msg) == std::tie(mc2.a, mc2.ch, mc2.msg);
}
bool operator==(MyClass2 const& mc2, MyClass1 const& mc1) {
return std::tie(mc1.a, mc1.ch, mc1.msg) == std::tie(mc2.a, mc2.ch, mc2.msg);
}
bool operator!=(MyClass1 const& mc1, MyClass2 const& mc2) {
return std::tie(mc1.a, mc1.ch, mc1.msg) != std::tie(mc2.a, mc2.ch, mc2.msg);
}
bool operator!=(MyClass2 const& mc2, MyClass1 const& mc1) {
return std::tie(mc1.a, mc1.ch, mc1.msg) != std::tie(mc2.a, mc2.ch, mc2.msg);
}
Because if you define assignment, you will to define a constructor or a conversion from one to the other.
/*explicit?*/ operator MyClass1() const& {return {a, ch, msg};} // need thios
and a move constructor?
/*explicit?*/ operator MyClass1() && {return {a, ch, std::move(msg)};} // need this
if one can be ordered the other also, and you will need to define order between the two classes, in both directions
// won't even try
// bool operator<
// bool operator<=
// bool operator>
// bool operator>=
// bool operator<
// bool operator<=
// bool operator>
// bool operator>=
and for that matter any function that work with one should work with the other, because well, when you assign you are saying that two things are logical equal among other tings.
full code here: https://godbolt.org/z/c8d34eT48
While it seems to be your case (albeit a very suspicious case), as you see, you open a Pandora's box by defining equality between two classes.
Just by calling the "assignment" convert instead you save your self a big headache. Don't use operator=. My recommended code is to just use another name:
MyClass2& convert(MyClass1 const& from, MyClass2& to) {
std::tie(to.a, to.ch, to.msg) = std::tie(from.a, from.ch, from.msg);
return to;
}
MyClass2& convert(MyClass1&& from, MyClass2& to) {
std::tie(to.a, to.ch, to.msg) = std::tie(from.a, from.ch, std::move(from.msg));
return to;
}
More material: https://www.youtube.com/watch?v=ABkxMSbejZI
Once you understand the drawbacks, std::tie can help you (as shown).
Also, if all classes are public and simple, Boost.PFR https://www.boost.org/doc/libs/1_78_0/doc/html/boost_pfr.html
struct iClass
{
int a;
char ch;
std::string msg;
// implement iClass == = operator
};
struct MyClass1 : virtual iClass{
// some virtual member functions
};
struct MyClass2 : virtual iClass {
// some virtual member functions
};
Should be able to compare by reintrepret_cast to iClass as well assignment.

Overloading the comparison operator for object members

Say I have a class called book:
class Book {
int i;
public:
Book(int ii)
: i(ii) {
}
int ISBN() {
return i;
}
};
I want to overload the comparison operator for the Book class, so that I can create a bool function that will compare the member "i" when it comes across book1==book2.
bool is_same() {
return (book1==book2) ? true : false;
}
How would I go about this? This is the current operator overload function I have, it gives me an "invalid initialization of non-const reference of type 'Book&' from an rvalue of the type 'bool'" error. I currently have my overloaded function inside of the class Book as a public function.
Book& operator==(const Book& b) const {
return ISBN() == b.ISBN();
}
I'm relatively new to operator overloading, I have sifted through many answers but none of them resolve my issue. I understand how one could simply do book1==book2, but that would only return true if every single member was of the same value. In this case I have more than just one, but I only want to return true if "i" is the same for both objects.
You basically have 2 choices:
use a member operator with one argument:
class Book {
...
bool operator==( const Book &an ) const { return ISDN() == an.ISDN(); }
};
use a non-member operator (and possibly a friend statement) with 2 arguments:
bool operator==( const Book &b1, const Book &b2 )
{
return b1.ISBN() == b2.ISBN();
}
Note that ISDN() should be made const.
Either way, you need to return a bool, not a Book &, which is usually returned by the assignment operator =, not the comparison operator ==.

C++ strict weak ordering derived class

I'm trying to implement strict weak ordering in a subclass that I want to place in an STL set container. The STL set uses operator< to order its elements in strict weak ordering. In my case I have a hierarchy of types and I am not quite sure how to achieve this for derived types. To this end, I put together a quick live demo showing where I am uncertain. I am ordering the fields using an easy to use std::tie technique. My area of uncertainty is how I should call the superclass' operator< prior to calling the std::tie comparison on the derived fields.
struct Base {
Base(const int& rIntVal, const std::string& rStrVal)
: mIntVal(rIntVal)
, mStrVal(rStrVal)
{}
inline bool operator<(const Base& rhs) const {
return std::tie(mIntVal, mStrVal) < std::tie(rhs.mIntVal, rhs.mStrVal);
}
private:
int mIntVal;
std::string mStrVal;
};
struct Derived : public Base {
Derived(
const int& rIntVal,
const std::string& rStrVal,
const std::string& rOtherStrVal,
const std::string& rOtherStrVal1)
: Base(rIntVal, rStrVal)
, mOtherStrVal(rOtherStrVal)
, mOtherStrVal1(rOtherStrVal1)
{}
inline bool operator<(const Derived& rhs) const {
// not sure what to do here - this is my best guess???
if( Base::operator<(rhs) ) {
return std::tie(mOtherStrVal, mOtherStrVal1) <
std::tie(rhs.mOtherStrVal, rhs.mOtherStrVal1);
} else {
return false;
}
}
private:
std::string mOtherStrVal;
std::string mOtherStrVal1;
};
You would do best to tie a reference to the base class:
bool operator<(const Derived& rhs) const {
return std::tie(static_cast<const Base&>(*this), mOtherStrVal, mOtherStrVal1) <
std::tie(static_cast<const Base&>(rhs), rhs.mOtherStrVal, rhs.mOtherStrVal1);
}
This will compare by the superclass fields first and then by the subclass fields.
Firstly, you could choose to have the derived fields take priority over the base ones, so that the derived members are considered first, or you could give the base fields priority. Which to do depends on the meaning of your class and how it should be sorted.
You have chosen to compare the base fields first, which is fine, so we'll go with that.
To be a strict weak ordering you should only compare the derived fields when the base sub-objects are equivalent (i.e. neither is less-than the other).
With your current code if you have lhs.mIntVal < rhs.mIntVal you should return true, but instead you go on to compare the derived fields, and might end up saying that lhs is not less than rhs even though the the result from the base class says it is.
So to make the result correct you need something equivalent to:
bool operator<(const Derived& rhs) const {
if (Base::operator<(rhs))
return true;
else if (rhs.Base::operator<(*this))
return false;
// base parts are equivalent, compare derived parts:
return std::tie(mOtherStrVal, mOtherStrVal1) <
std::tie(rhs.mOtherStrVal, rhs.mOtherStrVal1);
}
This is logically correct, but sub-optimal because you call Base::operator< twice. You could avoid that by including the base objects in the tie expression as ecatmur shows.

C++ Vector copying works with one vector but doesn't with the other

This problem is really odd, I can't seem to find anything that causes it.
So here is an assignment operator overloading function, birds and mammals are vectors. (down below are the classes)
const Register& Register::operator=(const Register& theOther)
{
if(this != &theOther)
{
this->~Register(); // deleted
birds.resize(theOther.birds.size());
mammals.resize(theOther.mammals.size());
birds=theOther.birds;
mammals=theOther.mammals;
}
return *this;
}
(if you find anything that is weird here, that is because I rewrote these lines many times). So the problem is while running, the
birds.operator=(theOther.birds);
works absolutely fine, every variable is copied properly, but when it reaches the next line, it calls the same vector copying function as with the birds, but mammals never gets theOther.mammals -es Animal parts (species, etc.). It gets the only mammal parts (predator, etc.). I just don't understand. Below are my classes, and as you can see, they are completely similar at the parts which are important here...
So, the classes
class Animal
{
char* species;
unsigned int weight;
char* color;
public:
...
}
The bird class
class Bird: public Animal
{
bool singing;
bool waterfowl;
unsigned int eggs;
public:
...
const Bird& operator =(const Bird& theOther);
{
if(this != &theOther)
{
Bird copy(theOther);
this->setSpecies(copy.getSpecies());
this->setWeight(copy.getWeight());
this->setColor(copy.getColor());
singing = theOther.singing;
waterfowl = theOther.waterfowl;
eggs = theOther.eggs;
}
return *this;
}
The mammal class
class Mammal: public Animal
{
bool predator;
bool oviparous;
public:
...
const Mammal& Mammal::operator =(const Mammal& theOther)
{
if(this != &theOther)
{
Mammal copy(theOther);
this->setPredator(copy.getPredator());
this->setOviparous(copy.getOviparous());
predator = theOther.predator;
oviparous = theOther.oviparous;
}
return *this;
}
And of course the Register, which has the vectors.
class Register
{
std::vector <Bird> birds;
std::vector <Mammal> mammals;
std::vector <Reptile> reptiles;
public:
...
edit:
Register::Register(const Register& newRegister)
{
birds = newRegister.birds;
mammals = newRegister.mammals;
reptiles = newRegister.reptiles;
}
edit2:
void Animal::setSpecies(char* _species)
{
this->species = _species;
}
Maybe I'm just too tired to find the mistake that I made. Please tell me what I did wrong.
As stated before - don't call the destructor manually.
The reason your mammal operator= doesn't copy the species, weight and color while your bird operator= does is because the mammal version doesn't have the code that sets those value. The bird operator has these lines:
this->setSpecies(copy.getSpecies());
this->setWeight(copy.getWeight());
this->setColor(copy.getColor());
the mammal operator does not.
Either copy those lines into the mammal operator or, better still, write a method of animal that does it and call it from both bird and mammal operators.
I am also not sure why you need to make a copy before calling getSpecies etc, you should be able to call it on the original theOther parameter.
Your code has undefined behaviour. After you call this->~Register();, you must not access any members of this. You should simply remove that line, and everything will be fine (although perhaps not exception safe).
If you want strong exception safety, you should implement operator= using the copy-and-swap idiom:
const Register& Register::operator=(Register theOther)
{
std::swap(*this, theOther);
return *this;
}
Furthermore, the operator= in your subclasses should call through to the operator= in the super-class. You can do this manually with a call to Animal::operator=, or you can again use copy-and-swap for strong exception safety.
Mammal& Mammal::operator=(const Mammal& theOther)
{
if(this != &theOther)
{
static_cast<Animal&>(*this) = theOther; //Call Animal::operator=
predator = theOther.predator;
oviparous = theOther.oviparous;
}
return *this;
}
//Or use:
Mammal& Mammal::operator=(Mammal theOther)
{
std::swap(*this, theOther);
return *this;
}

implementing operator== when using inheritance

I have a base class which implements the == operator.
I want to write another class, inheriting the base class, and which should reimplement the == operator.
Here is some sample code :
#include <iostream>
#include <string>
class Person
{
public:
Person(std::string Name) { m_Name = Name; };
bool operator==(const Person& rPerson)
{
return m_Name == rPerson.m_Name;
}
private:
std::string m_Name;
};
class Employee : public Person
{
public:
Employee(std::string Name, int Id) : Person(Name) { m_Id = Id; };
bool operator==(const Employee& rEmployee)
{
return (Person::operator==(rEmployee)) && (m_Id == rEmployee.m_Id);
}
private:
int m_Id;
};
void main()
{
Employee* pEmployee1 = new Employee("Foo" , 1);
Employee* pEmployee2 = new Employee("Foo" , 2);
if (*pEmployee1 == *pEmployee2)
{
std::cout << "same employee\n";
}
else
{
std::cout << "different employee\n";
}
Person* pPerson1 = pEmployee1;
Person* pPerson2 = pEmployee2;
if (*pPerson1 == *pPerson2)
{
std::cout << "same person\n";
}
else
{
std::cout << "different person\n";
}
}
This sample code give the following result :
different employee
same person
Where I would like, even when handling Person* pointers, to make sure they are different.
How am I supposed to solve this problem ?
Thanks !
What you want to do is essentiall "virtualize" the comparison operator.
Since operators cannot be virtual (operators can be virtual), you will need to delegate it to something else. Here's one possible solution.
class Person
{
public:
/* ... */
bool operator==(const Person& rhs)
{
return m_Name == rPerson.m_Name && this->doCompare(rhs);
}
private:
virtual bool doCompare() = 0;
};
}
class Employee : public Person
{
/* ... */
private:
virtual bool doCompare(const Person& rhs)
{
bool bRetval = false;
const Employee* pRHSEmployee = dynamic_cast<const Employee*>(&rhs);
if (pEmployee)
{
bRetval = m_Id == pRHSEmployee->m_Id
}
return bRetval;
}
};
The question didn't make clear whether Person needs to be a concrete class. If so, you can make it not pure-virtual, and implement it to return true.
This also uses RTTI, which you may or may not be happy with.
Add a virtual function int Compare(const Person& rPerson) and use that in your operators
You still have a major problem if you have one person and one employee - the person may compare equal to the employee, but not the employee to the person. i.e:
(employee == person) != (person == employee)
This is a bad thing (tm). Basically you've made an equality operator that isn't symmetric
Edit:
Ok, no virtual operators - add the virtual Compare function suggested elsewhere I think - but you still have the symmetric problem.
There is no neat solution to this problem.
Which is not a problem actually, in C++. What sense does it makes to compare entities on a equality basis?
EDIT: a few links to meditate regarding the pertinence of equality applied to entities:
Objects Of Value, Kevlin Henney
Secrets Of Equals, by Angelika Langer, check "Entities vs values" section
EDIT2 (2018 nov 27th):
There is another problem here, problem which also has its root in OO design, not in C++. It's impossible to design a comparison operator that is reflexive (x == x), symmetric (x == y <=> y == x), and transitive (x == y && y == z => x == z) , that also complies with Liskov Substitution Principle. There is a thorough demonstration of this limitation in Joshua Bloch's Effective Java, 2nd ed.
TL;DR: Let's say we have ColouredPoint that inherits from Point, a function that works on references to Points and that needs to compare them. If {1,2} == {1,2,blue} , we will end up with {1,2,blue} == {1,2,green}. Or we refuse to compare points with coloured points, which breaks LSP. And so on. There is no solution. My conclusion is that inheriting comparison, while it's a appealing, it doesn't work.
The big question here is - how do you determine equality?
Can any object be compared to any other object in the hierarchy? Can only objects of the same type be compared? Where does the criteria for the comparison live?
The solution implementation will depend on the answers to these questions.
It doesn't make sense to have the same person equal two different employees but that is what you class design allows. You are better off arranging for identity to be attached to a person. You then ask if a.identity() == b.identity().
In order to make operator== symmetric you have to have a person and employee with the same shared details differ so that:
Person p("Foo");
Employee e("Foo" , 1);
p == e; // false
e == p; // false
This is unintuitive but necessary.
To do this you can use the typeid keyword
bool operator==(const Person& other) const
{
return m_Name == other.m_Name && typeid(other) == typeid(*this);
}
Of course Person must be a polymorphic type (have at least one virtual function).
You need to make Person::operator== virtual.
You can also take operator== outside of the class scope. In which case, you can either create necessary overloads or make it generic by way of templates.