March 8, 2002
Reading assignment:
  Deitel and Deitel - Read 8.5
  Other texts - overloading the << and >> operators

Overloading the << operator
  - We'll overload the << operator for the class Complex, shown here

    class Complex {
	friend ostream &operator<<(ostream &out, const Complex &c);
	friend istream &operator>>(istream &in, Complex &c);
    public:
        Complex(float r=0, float i=0) {real=r, imag = i;}
        Complex operator+(const Complex &c) const;
        bool operator==(const Complex &c) const {return (c.real==real &&c.imag==imag);};
	// etc., other member functions left off for brevity
     private:
        float real;
        float imag;
     };

     Complex Complex::operator+(const Complex &c) const
     {
         Complex ans;
         ans.real = real+c.real;
         ans.imag = imag+c.imag;
         return ans;
     }

     ostream &operator<<(ostream &out, const Complex &c)
     {
         out << c.real;
         if (c.imag) cout << " + " <<  c.imag << 'i';
         return out;
     }
 
     istream &operator>>(istream &in, Complex &c)
     {
         float real_part(0), imag_part(0);
         char ch1, ch2;
         // read in the real part, the plus sign, the imaginary part, and an 'i'
	 in >> real_part >> ch1 >> imag_part >> ch2;
	 c.real = real_part;
	 c.imag = imag_part;
         return in;
     }

  - Remember that if we say x op y, the compiler translates it to x.operator op(y)
    In other words, the member function operator op is called for the left operand
    But when we say cout << obj, the left operand is cout, not obj.  This means that
    we can't implement it as a member function of obj.  Instead, we overload << with
    a non-member function.  If such a function has been written, then the compiler will
    turn x.operator op(y) to operator op(x,y)
  - This non-member function must take the two operands as parameters.  Remember that
    cout is an ostream, so the first parameter is an ostream.  This first parameter
    cannot be const, since we are altering it by writing to it!  The second parameter is
    the object, in our case, a Complex object.  It should be const, because the object
    itself should not be altered just because we are displaying it.
  - We want to be able to cascade << (like the case of the assignment operator),
    so the function must return a reference to the ostream
  - Since the function is no longer a member function, we don't have to worry about
    whether the function itself should be declared const
  - The declaration is thus
    ostream &operator<<(ostream &out, const Complex &c);
  - If we make this a friend function, then it can access the data members
    and display them
  - Now we can output complex numbers with code such as:
    Complex c1(2,4);
    cout << c1;

Overloading the >> operator
  - We'll now overload the >> operator for the class Complex
  - Again, this non-member function must take the two operands as parameters.  Remember
    that cin is an istream, so the first parameter is a (non-const) istream &.
    As before, the second parameter is a Complex &, but this time we expect the second
    parameter to be changed, so it is no longer const
  - We want to be able to cascade >> as well, so the function must return a reference
    to the istream
  - The declaration is thus
    istream &operator>>(istream &input, Complex &c);
  - If we make this a friend function, then we can set the private data
    members to the values input from the stream
  - Look at the function definition - note that it currently does no error checking
    and requires you to enter a real and imaginary part (even for 0+0i)
  - Now we can output complex numbers with code such as:
    Complex c1;
    cin >> c1;