Overloading Operators and Friends
Overloading Operators
  We created a class to declare and define the data
  type Fraction. Nonetheless, our programs still seem a little artificial when using
  fractions. For example, when we working with integers, we can have
  statements such as
  
    int num1, num2, num3;
    ...
    num3 = num1 + num2;
  
  
  but when working with fraction objects using
  our Fraction class from the last lab, the statements
  would be:
  
    Fraction F1, F2, F3;
    ...
    F3 = F1.add(F2);
  
  The program would be more readable if we could have used the statement
    F3 = F1 + F2;
  
An operator that operates on two
  values is called a binary operator. The binary operator
  + has a 
  well-defined 
  meaning (addition) when the two operands (the objects to the
  left and right of the operator) are integers, and C++ uses that
  meaning. But, when the operator is used with a user-defined class,
  the meaning of the operator is unclear to C++. The same is
  true of
  the other usual binary operators we use with integers and other
  built-in types, e.g., -
  for subtraction, * for multiplication, /
  for division, && for logical
  and, || for logical or, < for "less
  than", <= for "less than or equal to", etc.
C++ provides a mechanism for being able to provide a meaning for
  all of the above operators for any data type defined by a user using
  a class. This mechanism is called operator overloading, since
  we are giving an additional meaning to an operator, i.e.,
  overloading the meaning of the operator. Once overloaded for a
  fraction object, the operator + has one meaning (the
  usual addition of integers) if both the operands are integers, but
  has a different meaning (the one supplied in the user-defined class
  for fraction objects) when the two operands are fraction objects.
The header and program
  files Fraction.h
  and Fraction.cc have the same
  class declaration and definition as the last lab, with the addition
  of a declaration and definition for an overloaded
  operator +. The program
  file UseFract.cc contains a
  program that uses this Fraction class and demonstrates
  the use of the overloaded operator for Fraction
  objects. Study the declaration and definition of the overloaded
  operator. They are the same as the corresponding declaration and
  definition of the member function add, except that the
  name of this member function (the overloaded operator) is
  operator +. Note the usage of the operator in the
  program useFract.cc. Do you expect the values of the
  three results to be different?
When the statement
    Fraction result1 = f1 + f2;
  
  
  is executed, the overloaded operator + is
  called; f1 is the implicit parameter to the operator,
  and f2 is copied as the value of the formal
  parameter pFract. In general, any overloaded binary
  operator in a class has one formal parameter.   When using the
  overloaded binary
  operator, the operand on the
  left of the usage of the binary operator is the implicit parameter
  in the operator call, and the operand on the right is the actual
  parameter.
Note the addition of const for the 
  the function add as well as
  the overloaded operator, and their formal parameter. The member
  function add and
  the overloaded operator do not change the value of the formal
  parameter, nor do they change the value of the implicit parameter;
  hence the const for the functions and their formal
  parameters.
As noted above, any of the usual binary operators can be overloaded. The
  return type of the operator should be the expected type, i.e., for
  addition of fractions, the return type should
  be Fraction, but the return type of the
  operator < should be bool. Thus, in the
  class declaration, the
  declaration of the overloaded operator < will be
  
    bool operator < (const Fraction pFract) const;
  
  How would you write the definition of the operator?
When one defines a class, the
  operators = (assignment) and == (equality
  check) are both defined for the class. This default =
  operator simply assigns the respective
  member data objects, using the = operator for the types
  of the member data objects. The default == operator
  simply compares the respective member data objects using
  the == operator for the types of the member data
  types. This is the reason why, if a class has an array, we cannot
  use the = and == operators correctly. 
In the program useFract.cc, when the statement
  
    Fraction result2 = f1 + f2;
  
  
  the default = operator is called two times: once to
  copy the value of f2 to the formal
  parameter pFract, and then to copy the return
  value of the + operator to the object result2.
Both these operators can be overloaded, and must be overloaded in a class if the class contains an array.
For the overloaded
  operator = the implicit parameter is the object on the
  left of the operator, and the actual parameter is the object on the
  right. For example, if the operator = were overloaded
  for the Fraction class, when the statement
  
    result1 = fraction1;
  
  
  is executed (where result1 and fraction1
  are both of type Fraction), result1 will
  be the implicit parameter to the operator call,
  and fraction1 will be the actual parameter. So, should
  the declaration of the operator be the following?
  
    void operator = (const Fraction pFract) const;
  
  
  There are two problems here. Firstly, the implicit parameter will
  change so the function should not be
  a const. Secondly, consider the execution of the above
  assignment statement using this overloaded
  operator. The = operator will be called to copy the
  value of fraction1 to the
  object pFract. This is circular, since we are declaring
  the overloaded operator. So, the formal parameter to the overloaded
  assignment operator is always passed by reference. Thus, the correct
  declaration for the operator is
  
    void operator = (const Fraction & pFract);
  
  
  Is the return type of void correct?
Although, the above declaration will not give any errors, and assignment statements like the one above will execute correctly, it will not work like the usual assignment operator where chaining the assignment operator is valid in C++. For example, the statement
    int x, y, z;
    ...
    x = y = z;
  
  
  causes x and y to get the value
  of z. But, the statement
  
    fraction1 = fraction2 = fraction3;
  
  will not work. This is because C++ treats the above statement as
    fraction1 = (fraction2 = fraction3);
  
  
  Since the return value of our = operator
  is void, the expression in parentheses does not have
  any value. The correct way to declare the operator, so that a
  chained assignment statement as above will work is
  
    Fraction & operator = (const Fraction & pFract);
  
  
  The return type here is a reference to a Fraction
  object. Now, in the definition of the operator, besides copying the
  member data objects from the formal parameter to the implicit
  parameter, the implicit parameter needs to be
  returned by reference. The keyword this in any member
  function is a reference to the implicit parameter. So, the return
  statement in the assignment operator should be:
  
    return (*this);
  
  
  The * before this is the C++ syntax for
  de-referencing a reference, and thus get the object that is being
  referenced. So, the return value is the implicit
  parameter object, but because of the return type
  being Fraction & the returned value is a reference
  to the implicit parameter. Try to write the definition of the
  assignment operator.
The equality operator obviously should have a return
  type bool. Neither the implicit parameter object, nor
  the formal parameter object will be changed by the operator. So,
  what would the header for the operator be?
The implementation of the equality operator will need to compare the individual member data objects using the appropriate equality check for each of their types. Thus, it is useful to define the equality operator for every user-defined type.
If any of the member data objects are arrays, then the equality operator implementation must compare all the individual elements of the arrays for equality.
Overloading Input/Output Operators
C++ allows the input and output operators >>
and <<, respectively, to be overloaded as well, but
the implementation is a little different than the above operator
overloads.
Consider the input statement
    cin >> num;
  
  
  Clearly, >> is a binary operator. On the left of
  the operator is an input stream, and on the right of the operator is
  an object. The type name, given to us by the iostream
  library, for input streams is istream, and the type
  name for output streams is ostream.
Recall that when we overloaded the operator + and
  called it, the operand on the left was passed as the implicit
  parameter, and thus, the operator could be a member function. For
  the input and output operators, the operand on the left is not of
  the type of the user-defined type for which we want to define the
  operator. For example, if we were to overload
  the >> operator for the Fraction
  class, when we call the operator to output a fraction object, the
  operand on the left of the operator would be of
  type ostream, and the 
  type of the operand on the right of the operator would
  be Fraction. For this reason, overloaded input and
  output operators for a class are not member functions of that
  class. They should thus be declared in a separate .h
  file, and implemented in a corresponding .cc file.
Also, as we saw with the = operator above, we would
  like to be able to chain the output operator as is legal in
  C++. That, is we would like to be able to write the statement
  
    cout << fraction1 << " and " << fraction2 << endl;
  
  In this case, though, C++ treats such a chained statement as if it were parenthesized from the left, i.e., the statement
    cout << x << y << endl;
  
  as
    ((cout << x) << y) << endl;
  
  
  where each parenthesized statement is an expression whose value is
  an output stream. Thus, just as we did in the assignment operator,
  the return type of the overloaded << operator
  should be a reference to the output stream.
Study the header for the overloaded output operator
  for Fraction objects in the
  file FractionIO.h. Since
  this is not a member function, we must specify two formal
  parameters, one for each of the two operands. When the operator is
  used, the operand on the left of the operator is the
  actual
  parameter corresponding to the first formal parameter, and the
  operand on the right of the operator is the actual
  parameter corresponding to the second formal parameter. C++ passes
  parameters in this way since the function was declared to be an
  operator.
Note that the formal parameter stream is passed by
  reference since the stream is changing in the function. Note also
  that the object to be output is passed by reference as
  a const so that we do not make a copy of the actual
  parameter object
  when the operator is called.
Why do we need to include Fraction.h and
  the iostream library in FractionIO.h?
Study the definition for the overloaded output operator
  for Fraction objects in the
  file FractionIO.cc. Since
  this is not a member function, it does not have access to the
  private member data objects of the Fraction object. So,
  it has to use the accessor functions provided by the class.
The operator returns the
  parameter stream, not *stream as we
  did in the assignment operator,  since stream is a
  parameter passed by reference, and so is already the object.
Try using the above overloaded output operator in the program
  in useFract.cc.
Friend functions
We saw above that because of the nature of the operands, the input
  and output operators for a class cannot be member functions
  of that class. At the same time, the input and output
  operators, by their very nature, need to access the private
  member data objects of the class. C++ provides a mechanism
  of friend functions. We can make the above overloaded
  output operator a friend of the Fraction
  class by adding the header for the operator in the class
  declaration, and adding the keyword friend at the
  beginning of the header. So, in the class declaration, the header
  will be:
  
    friend ostream & operator << (ostream & stream, const Fraction & pFract);
  
  
  Now, we will not need the file FractionIO.h. Similarly,
  we could add the definition for the operator without the keyword
  friend in the
  file Fraction.cc that contains the implementations for
  the class functions. This implementation will look exactly as it
  does in the file FractionIO.cc, except in this
  implementation, we can access the private member data objects
  directly, and thus do not have to call the accessor functions. Now,
  we will not need the 
      file FractionIO.cc either.
Try including the above output operator as a friend function of
  the Fraction class as described above, and then try
  compiling the useFract.cc program and running it to
  make sure that the behaviour is the same as before.