Chapter 17 - Operator Overloading


Chapter Goals



17.1  Operator Overloading


17.1  Operator Overloading


17.1  Operator Overloading

To define an operator:

Overloadable Operators

+ - * / % ^ &
| ~ ! = < > +=
-= *= /= %= ^= &= |=
<< >> <<= >>= == != <=
>= && || ++ -- ->* .
-> [] () new new[] delete delete[]

17.1  Operator Functions


17.1  Operator Functions (cont.)

Syntax 17.2 : Overloading Operator Definition
return_type operatoroperator_symbol( parameters )
{
	 statements
}
Example:
int operator-( Time a, Time b )
{
 return a.seconds_from( b )
}
Purpose: Supply the implementation of an overloaded operator.

17.1  Example - Operator Functions


17.1  Operator Overloading

Avoid ambiguous behavior:
some_return_type operator+(Time a, Time b);

17.1  Operator Overloading (cont.)

Caveat: Units are not implied by the context. Add minutes? Seconds?


17.1  Operator Member Functions

Syntax 17.2 : Overloading Operator Member Function Definition
return_type ClassName::operatoroperator_symbol( parameters )
{
   statements
}
Example:
Time Time::operator+( int sec )
{
   Time r = *this;
   r.add_seconds( sec );
   return r;
}
Purpose: Supply the implementation of an overloaded operator member function.

17.1  Operator Member Functions


17.1  Example - Operator Member Functions

The addition operator as a member function of the Time class:
class Time
{
   ...
   Time operator+( int sec ) const;
};

Time Time::operator+( int sec ) const
{
   Time r = *this;  // Copy the implicit parameter
   r.add_seconds( sec );
   return r;
}

17.1  Operator Overloading - member vs. global


17.2  Case Study: Fractional Numbers

New type to represent a ratio of 2 integers:

Fraction a( 3, 4 ); // Represents 3/4
Fraction b( 7 ); // Represents 7/1

Fractions should behave as other numbers:

Fraction c( 1, 2 );
   if( a < b )
      c = b - a;
   else
      c = a - b;
   cout << "Value is " << c << "\n";

17.2  Case Study: Fractional Numbers

Should be able to mix w/other numbers:

c = a + 3;  // Should mean same as addition of 3/1
double x = 2.5 * a;  // Should convert fraction to a double

17.2  Case Study: Fractional Numbers - Construction


17.2  Case Study: Fractional Numbers (fraction.h)


17.2  Case Study: Fractional Numbers (fraction.cpp)

(Discussion follows)


17.2  Case Study: Fractional Numbers (fractiontest.cpp)


17.3 Overloading Simple Arithmetic Operators


17.3 Simple Arithmetic Operators - Fraction Example

a/b + c/d is defined to be (a*d + c*d)/(b*d)

Fraction operator+(const Fraction& left, const Fraction& right)
{
   Fraction result(left.numerator() * right.denominator()
      + right.numerator() * left.denominator(),
      left.denominator() * right.denominator());
   return result;
}

The remaining operations are defined similarly


17.3 Unary Arithmetic Operators


17.4 Overloading Comparison Operators


17.4 Overloading Comparison Operators


Productivity Hint 17.2


Define Comparisons in Terms of Each Other


Advanced Topic 17.2


Symmetry and Conversion


17.5 Overloading Input >> and Output <<


17.5.1 Stream Output <<


17.5.2 Stream Input >>


17.5.2 Stream Input >> (cont.)

Assume a Time is input as 3 separate integers, 9 15 00 to represent 9:15:00 a.m.

istream& operator>>(istream& in, Time& a)
{
   int hours;
   int minutes;
   int seconds;
   in >> hours >> minutes >> seconds;
   a = Time(hours, minutes, seconds);
   return in;
}

Advanced Topic 17.3


Peeking at the Input

You may put a single character back into the stream.

E.g.: We want to allow a user to input a Fraction as a single integer (7), or as an integer, slash, integer (3/4)


Advanced Topic 17.3 (cont.)


istream& operator>>(istream& in, Fraction& r)
{
   int t, b;
   // Read the top
   in >> t;
   // If there is a slash, read the next number
   char c;
   in >> c;
   if (c == '/')
      in >> b;
   else
   {
      in.putback(c);
      b = 1;
   }
   r = Fraction(t, b);
   return in;
}

17.6  Overloading Increment and Decrement Operators


17.6  Overloading Increment and Decrement Operators


17.6  Overloading Increment and Decrement Operators (cont.)

Fraction& Fraction::operator++()
{
   top += bottom;
   return *this;
}
Fraction Fraction::operator++(int unused)
{
   Fraction clone(top, bottom);
   top += bottom;
   return clone;
}

17.6.1 Iterators and Overloaded Operators


17.6.1 Example: Implementing Iterator Operators

Recall Iterator, defined on our List of strings. We can now overload the standard operators:

Iterator& Iterator::operator++(int)
{
   position = position->next;
   return *this;
}

string Iterator::operator*() const
{
   assert(position != NULL);
   return position->data;
}

17.6.1 Example: Implementing Iterator Operators (cont.)

bool Iterator::operator==(const Iterator& b) const
{
   return position == b.position;
}

bool Iterator::operator!=(const Iterator& b) const
{
   return position != b.position;
}

17.7 Overloading the Assignment Operators

The Assignment Operator,   =


17.7.1 Overloading Compound Assignment Operators  (+= *=, etc.)


17.8 Overloading Conversion Operators


17.8 Overloading Conversion Operators


17.8 Overloading Conversion Operators

Note:   The compiler performs at most one level of user-defined type conversion when matching an overloaded function.

17.8 Example - Overloading Conversion Operators

Convert a Fraction to a double:
Fraction::operator double() const
{
   // Convert numerator to double, then do division
   return static_cast<double>(top) / bottom;
}
Can be used:
Fraction a(1, 2);
double d = 7.0;
double halfd = d * a; // a is converted to double to do multiplication
cout << "one half seven is " << halfd << "\n";

17.8.1 Stream Loops and Conversion Operators

Consider the familiar construct:
while( cin >> x )
   . . .

17.9 Overloading the Subscript Operator


17.9 Example: Overloading the Subscript Operator

Implementation of a "safe array":


17.9 Example: Overloading the Subscript Operator (cont.)

class SafeArray
{
public:
   SafeArray(int s);
   SafeArray(const int v[], int s);
   int& operator[](int i);
   int operator[](int i) const;
   private:
   int size;
   int* values;
};

SafeArray::SafeArray(int s) : size(s), values(new int[size]) {}

17.9 Example: Overloading the Subscript Operator (cont.)

SafeArray::SafeArray(const int v[], int s) : size(s)
{
   values = new int[size];
   for (int i = 0; i < size; i++)
   values[i] = v[i];
}

int& SafeArray::operator[](int index)
{
   assert((index >= 0) && (index < size));
   return values[i];
}

int SafeArray::operator[](int index) const;
{
   assert((index >= 0) && (index < size));
   return values[i];
}

17.10 Overloading the Function Call Operator


17.10 Example: The Function Call Operator


17.10 Example: The Function Call Operator

class RandomInt
{
public:
   RandomInt(int ia, int ib);
   int operator()();
private:
   int a, b;
};

RandomInt::RandomInt(int ia, int ib) : a(ia), b(ib) {}

int RandomInt::operator()()
{
   return a + rand() % (b - a + 1);
}

17.10 Example: The Function Call Operator

Declare an instance, then use it as a function:

RandomInt a(7, 15); // Return random values from 7 to 15
cout << "one random value " << a() << "\n";
cout << "and another " << a() << "\n";

17.10 Example 2: Variable arguments

class RandomInt
{
public:
   RandomInt(int ia, int ib);
   int operator()();
   int operator()(int nb);
   int operator()(int na, nb);
private:
   int a, b;
};

17.10 Example 2: Variable arguments (cont.)

RandomInt::RandomInt(int ia, int ib) : a(ia), b(ib) {}

int RandomInt::operator()()
{ return a + rand() % (b - a + 1); }

int RandomInt::operator()(int nb)
{ return a + rand() % (nb - a + 1); }

int RandomInt::operator()(int na, int nb)
{ return na + rand() % (nb - na + 1); }

17.10 Example 2: Variable arguments

The function selected will be determined by the number of arguments provided:

RandomInt r(3, 7);
cout << "random value between 3 and 7 " << r() << "\n";
cout << "random value between 3 and 10 " << r(10) << "\n";
cout << "random value between 23 and 30 " << r(23, 30) << "\n";

Advanced Topic 17.5


Other Operators

Operators not commonly used, hence, not commonly overloaded:

Advanced Topic 17.5


Other Operators (cont.)


Advanced Topic 17.6


Inline Functions

To define:

17.11 Case Study: Matrices

Operations on matrices:

17.11 Case Study: Matrices

Implementation:


17.11 Case Study: Matrices (cont.)


17.11 Case Study: Matrices (matrix1.h)


17.11 Case Study: Matrices (matrix1.cpp)

Implementation for the Matrix class:

17.11 Case Study: Matrices (matrixtest1.cpp)


Chapter Summary


  1. Define new meanings for C++ operators
  2. Member vs. nonmember operators
  3. Extend new types using stream I/O operators
  4. Simulate pointers by defining operators on iterators
  5. Operators to define behavior of conversions
  6. Function objects