I have two classes and one of them is inherited from the other class.
Class Person - used to store first name and last name of a person. The data members are protected.
Class Candidate - used to store votes of an election, and this is inherited from Person class so that I can store name of the candiadate.

I have overloaded these operators in Person class : ==, !=, <, <=, >, >=

I have also overloaded the same operators in Candidate class too but the definision are same because you can only use the name of the candidate to compare. So rather than copy and paste the same code, I used static_cast. It worked but I wonder is it bad to do like that?

Ex:

bool Candidate::operator ==(const Candidate& right) const
{
    //return (firstName == right.firstName && lastName == right.lastName);
    // Rather than the above, I used this...
    return (static_cast<Person>(*this) == static_cast<Person>(right));
}

Recommended Answers

All 14 Replies

Well, I would do the same thing, so I think it should be fine. I do a lot of casting in my programs as well and have faced no problems so far. Whether it is a bad practice or not, I don't think so. Not that I know of anyway.

Member Avatar for embooglement

If the operator's are also defined in the base class, Person, then why not just leave it at that? Unless the Candidate implementation actually does something different, I wouldn't overload the operators in the derived class at all.

Instead of

return (static_cast<Person>(*this) == static_cast<Person>(right));

you should write

return (*static_cast<Person*>(this) == *static_cast<Person*>(&right));

because otherwise you are asking the compiler to generate code that copies the objects "right" and "*this".

This is unnecessary code:

bool Candidate::operator ==(const Candidate& right) const
{
    //return (firstName == right.firstName && lastName == right.lastName);
    // Rather than the above, I used this...
    return (static_cast<Person>(*this) == static_cast<Person>(right));
}

If you do not define this operator in your derived Candidate class, the operator function from your base Person class will be used. I think you need to work on the concepts of inheritance a little bit.

Consider this:

#include <iostream>

using namespace std;

class Base
{
    int value;
public:
    Base( int value ) : value(value){}
    bool operator==( const Base& other )
    {
        return value == other.value;
    }
};

class Derived : public Base
{
public:
    Derived( int value ) : Base(value){}
};

int main()
{
    Base b0( 4 );
    Derived d0( 2 * 2 );
    Derived d1( 2 + 2 );
    cout << ( b0 == d0 ) << endl;
    cout << ( b0 == d1 ) << endl;
    cout << ( d0 == d1 ) << endl;
    return 0;
}

If you can understand why this works, you are on your way.

Yeah I was testing that and realised but lets say in the future if I want to use Candidate location to compare, I must overload the operators right(ie. operator ==)?? so if that was the case, it is ok to use the code like this right:

bool Candidate::operator ==(const Candidate& right) const
{
    return (location == right.location && static_cast<Person>(*this) == static_cast<Person>(right));
}

-------------------------------------------------------------------

I also have another problem, take a look at this code.

#include <iostream>
using namespace std;

class ClassA
{
public:
    bool operator==(const ClassA& right) const
    {
        return (x == right.x);
    }

    int x;
};

class ClassB : public ClassA
{
public:
    bool operator==(const ClassB& right) const
    {
        return (y == right.y && static_cast<ClassA>(*this) == static_cast<ClassA>(right) );
    }

    int y;
};

int main()
{
    ClassA a1;
    ClassB b1;

    a1.x = 1;

    b1.x = 1;
    b1.y = 2;


    cout << (a1 == b1) << endl;  // outputs 1
    cout << (b1 == a1) << endl; // Error: no match for ‘operator==’ in ‘b1 == a1’
}

if first cout statement outputs 1 why does second cout statement create an error. Theoritically both should work in same right?.. it's kinda funny too me..

Member Avatar for embooglement

The reason is because ClassA's == operator takes a reference to a ClassA as the left hand argument. Because an instance of ClassB has a ClassA part that can be pointed to, it can use the ClassA operator. (a1 == b1) is using ClassA's operator, because a1 is an instance of ClassA. (b1 == a1) is trying to find an appropriate operator in ClassB, but it can't because the only == operator in ClassB takes a reference to a ClassB, which a ClassA cannot fulfill.

The easiest fix to this problem is to add a copy constructor to ClassB that takes a ClassA. Then when you do (b1 == a1) it will call the copy constructor on a1, and build a new ClassB object to compare with b1 using ClassB's == operator.

Nope that don't work. When it creates a new ClassB object, what happens to the value of y of the new object?

ClassB(ClassA& obj)
    {
        x = obj.x;
        // y = ??? : From where can I get the value for y
    }

because the y is unknown, let's say y was assign 0 in this copy constructor. So now the first cout statement will output 1 and the second cout statement will out 0.
-_-... please enlighten, if i'm wrong. I guess I want to use a static_cast statement.

Member Avatar for embooglement

Yeah, that occured to me after I made the post. You could also type cast b1 back to an instance of ClassA. That could either be explicitly, or you include a typecast operator in ClassB.

Member Avatar for embooglement

Oh, or you could include another == operator in ClassB that takes a ClassA as an argument.

I hadn't noticed it before, but in general, defining symmetrical operators such as == as members is the road to madness. If you want them to have virtual properties, you should do something like this:

bool operator==(const Foo& a, const Foo& b)
{
    return a.equal(b);
}

and now you can define equal to do whatever typechecking you need on the type of b (noting that the type of a already determines which version of equal will be called).

I hadn't noticed it before, but in general, defining symmetrical operators such as == as members is the road to madness. If you want them to have virtual properties, you should do something like this:

bool operator==(const Foo& a, const Foo& b)
{
    return a.equal(b);
}

and now you can define equal to do whatever typechecking you need on the type of b (noting that the type of a already determines which version of equal will be called).

I partly agree.

I think the equality operator should be a member function of the class it will be operating on. Otherwise, it will have to be a friend function, which I avoid unless necessary. Furthermore, by making it a member function, you may also make it virtual, which I recommend.

Now, let's talk about the part I agree with! I also support making explicit, non operator member functions in the class that are used by the operators. I generally think of operator overloading as a convenience for any segment of code that externally manipulates an object. Within the class, however, I prefer using explicit functions.

Here's an example of how I typically implement this sort of functionality:

class Base
{
private:
    int b;
public:
    Base() : b(0){}
    Base( int b ) : b(b){}
    virtual ~Base(){}
    
    virtual bool equal( const Base& other ) const
    {
        return b == other.b;
    }
    virtual bool operator==( const Base& other ) const
    {
        return equal( other );
    }
};

class Derived
{
private:
    int d;
public:
    Derived() : Base(), d(0){}
    Derived( int d, int b ) : Base(b), d(d){}
    Derived( const Base& other ) : Base(other), d(0){}
    virtual ~Derived(){}
    
    virtual bool equal( const Derived& other ) const
    {
        return ( d == other.d ) && Base::equal( other );
    }
    virtual bool operator==( const Base& other ) const
    {
        return equal( other );
    }
};

Notice how within the classes I never directly use the operator==. Instead, I use the equal() function.

I think the equality operator should be a member function of the class it will be operating on. Otherwise, it will have to be a friend function, which I avoid unless necessary.

I still disagree.

If you make the equality operator a member, it is somewhere between difficult and impossible in general to avoid situations in which a==b compiles but b==a doesn't. Moreover, if you make the "equal" member function public--which I see no reason not to do, there is no need to make the equality operator a friend.

I still disagree.

If you make the equality operator a member, it is somewhere between difficult and impossible in general to avoid situations in which a==b compiles but b==a doesn't.

Making it a non-member destroys inheritance. If you want to virtualize equality operators, they need to be members. In regards to the == symmetry. I generally don't support writing equality functions that take differently typed arguments for the left and right side. Semantically, an object of type A should never be equivalent to an object of type B. If the equality of underlying data needs to be tested, or if you need to test functional equivalence, you should use explicit conversion/extraction functions in your equality logical expression. Alternatively, you could provide implicit conversion by specialized constructors.

IntegerObject A( 3 );
    RealObject B( 3.0 );
    
    A == B;    // Should only be valid if IntegerObject has
               // a constructor from a RealObject.  otherwise

    A == B.toInt();  // I prefer this approach.  Explicitness is nice, and
                     // there is symmetry in the equivalence arguments

My main point here is that the underlying semantics of an equivalence test expect that the type of left and right side arguments must be the same. I.E. An apple cannot equal an orange, even if they are the same size.

Making it a non-member destroys inheritance.

Not at all:

bool operator==(const T& a, const T& b) { return a.equal(b); }

causes a==b to have the same inheritance properties as a.equal(b) whenever a and b have type T or any type derived from T (assuming, of course, that the equal function is virtual and returns bool).

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.