One of the nice features of C++ is that you can give special meanings to operators, when they are used with user-defined classes. This is called operator overloading. You can implement C++ operator overloads by providing special member-functions on your classes that follow a particular naming convention. For example, to overload the + operator for your class, you would provide a member-function named operator+ on your class.


The following set of operators is commonly overloaded for user-defined classes:

= (assignment operator)
+ - * (binary arithmetic operators)
+= -= *= (compound assignment operators)
== != (comparison operators)





Assignment Operator {=}

The assignment operator has a signature like this:
  class MyClass {
  public:
    ...
    MyClass & operator=(const MyClass &rhs);
    ...
  }

  MyClass a, b;
  ...
  b = a;   // Same as b.operator=(a);

Notice that the = operator takes a const-reference to the right hand side of the assignment. The reason for this should be obvious, since we don't want to change that value; we only want to change what's on the left hand side.
Also, you will notice that a reference is returned by the assignment operator. This is to allow operator chaining. You typically see it with primitive types, like this:
  int a, b, c, d, e;

  a = b = c = d = e = 42;
This is interpreted by the compiler as:
  a = (b = (c = (d = (e = 42))));

==================================================================

Compound Assignment Operators {+= -= *=}

The important point is that these are destructive operators, because they update or replace the values on the left-hand side of the assignment. So, you write: MyClass a, b; ... a += b; // Same as a.operator+=(b)
In this case, the values within a are modified by the += operator.
How those values are modified isn't very important - obviously, what MyClass represents will dictate what these operators mean.

The member function signature for such an operator should be like this:

  MyClass & MyClass::operator+=(const MyClass &rhs) {
    ...
  }
We have already covered the reason why rhs is a const-reference. And, the implementation of such an operation should also be straightforward.
But, you will notice that the operator returns a MyClass-reference, and a non-const one at that. This is so you can do things like this:

  MyClass mc;
  ...
  (mc += 5) += 3;
Don't ask me why somebody would want to do this, but just like the normal assignment operator, this is allowed by the primitive data types. Our user-defined datatypes should match the same general characteristics of the primitive data types when it comes to operators, to make sure that everything works as expected.

This is very straightforward to do. Just write your compound assignment operator implementation, and return *this at the end, just like for the regular assignment operator. So, you would end up with something like this:

  MyClass & MyClass::operator+=(const MyClass &rhs) {
    ...   // Do the compound assignment work.

    return *this;
  }
As one last note, in general you should beware of self-assignment with compound assignment operators as well. Fortunately, none of the C++ track's labs require you to worry about this, but you should always give it some thought when you are working on your own classes.

=========================================================================

Binary Arithmetic Operators {+ - *}

The binary arithmetic operators are interesting because they don't modify either operand - they actually return a new value from the two arguments. You might think this is going to be an annoying bit of extra work, but here is the secret:

Define your binary arithmetic operators using your compound assignment operators.

There, I just saved you a bunch of time on your homeworks.

So, you have implemented your += operator, and now you want to implement the + operator. The function signature should be like this:

  // Add this instance's value to other, and return a new instance
  // with the result.
  const MyClass MyClass::operator+(const MyClass &other) const {
    MyClass result = *this;     // Make a copy of myself.  Same as MyClass result(*this);
    result += other;            // Use += to add other to the copy.
    return result;              // All done!
  }
Simple!
Actually, this explicitly spells out all of the steps, and if you want, you can combine them all into a single statement, like so:

  // Add this instance's value to other, and return a new instance
  // with the result.
  const MyClass MyClass::operator+(const MyClass &other) const {
    return MyClass(*this) += other;
  }
This creates an unnamed instance of MyClass, which is a copy of *this. Then, the += operator is called on the temporary value, and then returns it.
If that last statement doesn't make sense to you yet, then stick with the other way, which spells out all of the steps. But, if you understand exactly what is going on, then you can use that approach.

You will notice that the + operator returns a const instance, not a const reference. This is so that people can't write strange statements like this:

  MyClass a, b, c;
  ...
  (a + b) = c;   // Wuh...?
This statement would basically do nothing, but if the + operator returns a non-const value, it will compile! So, we want to return a const instance, so that such madness will not even be allowed to compile.
To summarize, the guidelines for the binary arithmetic operators are:

Implement the compound assignment operators from scratch, and then define the binary arithmetic operators in terms of the corresponding compound assignment operators.
Return a const instance, to prevent worthless and confusing assignment operations that shouldn't be allowed.

============================================================================

Comparison Operators == and !=

The comparison operators are very simple. Define == first, using a function signature like this:

  bool MyClass::operator==(const MyClass &other) const {
    ...  // Compare the values, and return a bool result.
  }
The internals are very obvious and straightforward, and the bool return-value is also very obvious.
The important point here is that the != operator can also be defined in terms of the == operator, and you should do this to save effort. You can do something like this:

  bool MyClass::operator!=(const MyClass &other) const {
    return !(*this == other);
  }
That way you get to reuse the hard work you did on implementing your == operator. Also, your code is far less likely to exhibit inconsistencies between == and !=, since one is implemented in terms of the other.

Designed By Blogger Templates | Templatelib & Distributed By Blogspot Templates