returnType operatorOperatorSymbol (parameterList);
int a; a = 4 + 2 + 3;
int Add(int i1, int i2); //adds i1 and i2, returns result int Equals(int& i1, int i2); //sets i1 equal to i2, returns new value of i1
Equals(a, Add( Add(4,2), 3) ) //function calls are evaluated from most deeply nested to least
Here is an example of explicitly calling the stream insertion operator on the string class (<<).Motivation
Example 1
int x = 3, y = 6, z; float a = 3.4, b = 2.1, c; z = x + y; c = a / b;
1/2 + 1/3 // evaluates to 5/6 2/3 - 1/3 // evaluates to 1/3
Fraction n1, n2, n3; n3 = n1 + n2; // will the compiler accept this?
int x = 5, y = 0; cout << x << y;
Fraction f(3,4); cout << "The fraction f = " << f << '\n';
friend Fraction Add(Fraction f1, Fraction f2);With this function prototype, a sample call would be:
Fraction n1, n2, n3; n3 = Add(n1, n2);
friend Fraction operator+(Fraction f1, Fraction f2);The usual function style call would look like this:
n3 = operator+(n1, n2);While this is legal, the advantage is being able to use the more common infix notation:
n3 = n1 + n2; // this becomes legal
Fraction operator+(Fraction f1, Fraction f2) { Fraction r; // declare a Fraction to hold the result // load result Fraction with sum of adjusted numerators r.numerator = (f1.numerator*f2.denominator) + (f2.numerator*f1.denominator); // load result with the common denominator r.denominator = f1.denominator * f2.denominator; return r; // return the result Fraction }
Fraction n1, n2, n3, n4, n5; n5 = n1 + n2 + n3 + n4; // now it is legal!
friend Fraction operator+(const Fraction& f1, const Fraction& f2);
Fraction Add(const Fraction& f) const;A sample call to this function:
Fraction n1, n2, n3; n3 = n1.Add(n2);
Fraction operator+(const Fraction& f) const;Again, we could use typical function notation, and the dot-operator:
n3 = n1.operator+(n2);But the whole point is to be able to use the more familiar notation, which still works, and no dot-operator required:
n3 = n1 + n2; // n1 is the calling object, n2 is the argument
Fraction Fraction::operator+(const Fraction& f2) const { Fraction r; // result r.numerator = (numerator * f2.denominator) + (f2.numerator * denominator); r.denominator = (denominator * f2.denominator); return r; }
// multiplication overload for Fractions friend Fraction operator*(Fraction f1, Fraction f2); // addition operator to add a Fraction and an integer friend Fraction operator+(Fraction f, int n); // same as above, but this one allows the int to come first in the call friend Fraction operator+(int n, Fraction f);
friend Fraction operator+(const Fraction& f1, const Fraction& f2);will take care of calls that involve type conversions from int to Fraction, via the conversion constructor:
Fraction n1, n2, n3; n3 = n1 + 5; n3 = 10 + n2;However, ask yourself... Will both of these work for the friend version and the member function version? Why or why not?
friend bool Equals(const Fraction& f1, const Fraction& f2);We can easily write this as an operator overload:
friend bool operator== (const Fraction& f1, const Fraction& f2);
Fraction n1, n2; if (Equals(n1, n2)) cout << "n1 and n2 are equal";Contrast with this:
Fraction n1, n2; if (n1 == n2) cout << "n1 and n2 are equal";
Fraction f; cout << f; // how would the machine know how to do this?
We have no reason to expect the second line to work! The insertion operator << is only pre-defined for built-in types. The iostream.h library doesn't know about the Fraction type.
The << operator is a binary operator (2 parameters, left side and right side). The first parameter is always an ostream object (we've mostly used cout, so far). Because of this, it cannot be defined as a member function (it would have to be a member of the ostream class, which we cannot change). The << and >> operators should always be defined as outside functions (usually friend functions). The second parameter is whatever new type it is being overloaded to print:
friend ostream& operator << (ostream& s, Fraction f);
This declaration has all of the usual parts for defining a function. The name is operator<< (the keyword operator and the operator symbol). The return type is ostream&. The parameters are (ostream& s, Fraction f). When defining overloads of << and >> , always pass the stream parameters by reference. A better way to write this operator is:
friend ostream& operator << (ostream& s, const Fraction& f);
Notice that the first one passes the Fraction by value (and makes a copy). The second passes by reference (avoiding the overhead of a copy). It is declared as a const because the Fraction does not need to change if we are just doing output.
Here is the corresponding prototype for extraction >>
friend istream& operator >> (istream& s, Fraction& f);
Notice that the Fraction parameter for >> is also a reference parameter. This is because we are getting input into the object, so we need to work on the original, not a copy.
Remember the Show() function of the Fraction class?
void Fraction::Show() { cout << numerator << '/' << denominator; }
Here is how the << operator might be defined for Fraction. Notice how similar it is to the Show() function.
ostream& operator << (ostream& s, const Fraction& f) { s << f.numerator << '/' << f.denominator; return s; }
Note the differences between this and the Show() function.
Once this is defined, we can use a Fraction object in a cout statement:
Fraction f1;
So now, instead of:
cout << "Fraction f1 is "; f1.Show(); cout << '\n';
We can write:
cout << "Fraction f1 is " << f1 << '\n';
Click here to see the Fraction class with the << overload used instead of Show().
Now, what would the definition of the >> overload look like for Fraction?
Try it!