More Classes

"The road to hell is paved with global variables" -- Steve McConnell

Overloading Background

Recall our discussion of overloaded functions:
int cube(int n)
{
  return n * n * n;
}
int i = 8;
long l = 50L;
float f = 2.5F;
double d = 3.14;

  // Works fine: 512
std::cout << cube(i) << std::endl;

  // May or may not work: 125000
std::cout << cube(l) << std::endl;

  // Not quite what we want: 8
std::cout << cube(f) << std::endl;

  // Not quite what we want: 27
std::cout << cube(d) << std::endl;
We solved this problem by using overloaded functions:
int cube(int n)
{
  return n * n * n;
}

float cube(float n)
{
  return n * n * n;
}
double cube(double n)
{
  return n * n * n;
}

long cube(long n)
{
  return n * n * n;
}
It will also work as expected without the user needing to chose the right function:
  // Works fine, calls cube(int): 512
std::cout << cube(i) << std::endl;

  // Works fine, calls cube(long): 125000
std::cout << cube(l) << std::endl;

  // Works fine, calls cube(float): 15.625
std::cout << cube(f) << std::endl;

  // Works fine, calls cube(double): 30.9591
std::cout << cube(d) << std::endl;
What we did was to provide the compiler with additional ways in which a value could be cubed.

We run into similar problems with operators. That is, operators may function differently when given operands of different types. For example:

3 + 4       ==> evaluates to an int, 7
3.1 + 4.1   ==> evaluates to a double, 7.2
2.4f + 5.1f ==> evaluates to a float, 7.5
3.1 + 4     ==> evaluates to a double, 7.1

How is this possible?

Other operators are also already overloaded for your enjoyment:

int i = 10;      
int *p = &i;      // * indicates p is a pointer, & is taking an address
int &r = i;       // & indicates r is a reference
int x = i & 7;    // & is the bitwise AND operator
int j = *p;       // * is used to dereference p
int k = i * j;    // * is used to multiply i and j
k = cube(4);      // () are used to call function
k = j * (3 + 4);  // () are used to group 
Since the compiler can deal with built-in types, it should be able to handle arrays of built-in types as well, don't you think? A lot of students new to C/C++ try to add two arrays as shown in the code snippet below. But this won't work.
int a1[5] = {1, 2, 3, 4, 5};
int a2[5] = {10, 20, 30, 40, 50};

  // we want a3 to have {11, 22, 33, 44, 55} 
int a3[5] = a1 + a2;  // this won't compile

  // add the elements one at a time
for (int i = 0; i < 5; i++)
  a3[i] = a1[i] + a2[i];  // this is correct
One explanation this fails is because a1 and a2 are addresses (pointers) and you can't add two pointers this way. Another explanation is that the '+' operator is not overloaded to add two static arrays like this.

Overloading Operators for User-Defined Types (Structs and Classes)

Suppose we have two StopWatch objects, and we want to make a third StopWatch object that represents the sum of the first two StopWatches:
StopWatch sw1(30); // 30 seconds
StopWatch sw2(10); // 10 seconds

  // sw3 should be 40 seconds
  //   but it is an error
StopWatch sw3 = sw1 + sw2;
The StopWatch class is a simple class that keeps track of how many seconds have elapsed. (The usefulness of the class is not important). We simply want a very simple class that can be used to demonstrate operator overloading.

Here's what the StopWatch class looks like:

Class definitionExamples
class StopWatch
{
  public:
      // Default constructor
    StopWatch();

      // Conversion constructor
    StopWatch(int seconds);

      // Non-default constructor
    StopWatch(int hours, int minutes, int seconds);

    void Increment(int seconds = 1);
    void Reset();
    int GetSeconds() const;
    void Display() const;

  private:
    int seconds_;
};
StopWatch sw1;
StopWatch sw2(625);
StopWatch sw3(9, 30, 0);

sw1.Display(); // 00:00:00
sw2.Display(); // 00:10:25
sw3.Display(); // 09:30:00

sw1.Increment();  // add 1 sec
sw1.Increment();  // add 1 sec
sw1.Increment(5); // add 5 secs
sw1.Display();    // 00:00:07
Implementation

What is sizeof(Stopwatch)?

This code:

  // Add two StopWatches
StopWatch sw3 = sw1 + sw2;
produces this error:
error: no match for 'operator+' in 'sw1 + sw2'
Notes:

Overloading the binary + Operator

To overload the + operator (or most any operator for that matter), we use an operator function, which has the form:
return-type operatorop(argument-list)
where: For example:
operator+(argument-list)
operator-(argument-list)
operator*(argument-list)
We simply need to create a function called operator+ that takes two StopWatch objects as parameters:
StopWatch operator+(const StopWatch& lhs, const StopWatch& rhs)
{
    // lhs is left-hand side
    // rhs is right-hand side
  StopWatch sw(lhs.GetSeconds() + rhs.GetSeconds());

    // Return the result by value (a copy)
  return sw;
}
Compare this to our original "C-style" function:
StopWatch AddStopWatch(const StopWatch& sw1, const StopWatch& sw2)
{
    // Construct a new StopWatch from two
  StopWatch sw(sw1.GetSeconds() + sw2.GetSeconds());

    // Return the result by value (a copy)
  return sw;
}
This makes life much simpler for the users of the StopWatch class. Example usage:
StopWatch sw1(30); // 30 seconds
StopWatch sw2(10); // 10 seconds
StopWatch sw3(60); // 60 seconds
StopWatch sw4(20); // 20 seconds

StopWatch sw5 = sw1 + sw2;
StopWatch sw6 = sw1 + sw2 + sw3 + sw4;

sw5.Display(); // 00:00:40
sw6.Display(); // 00:02:00

  // Functional notation (this is really what's happening at runtime)
StopWatch sw7 = operator+(sw1, sw2);
sw7.Display(); // 00:00:40
You can kind of think of it as the compiler re-writing your code:
StopWatch sw5 = sw1 + sw2;   ====>   operator+(sw1, sw2);
In practice, the functional notation is seldom used. Essentially, overloading an operator means writing a function that the compiler will call when it sees the operator being used with a non-built-in (i.e. user-defined) data type (struct/class).

Again, notice how similar the functional notation is to our original function:

StopWatch sw7 = operator+(sw1, sw2);
StopWatch sw8 = AddStopWatch(sw1, sw2);
And these are identical:
StopWatch sw7 = sw1 + sw2;
StopWatch sw7 = operator+(sw1, sw2);

Overloading More Operators in the StopWatch Class

While we're at it, we'll overload some more operators. First, the subtraction operator:
StopWatch operator-(const StopWatch& lhs, const StopWatch& rhs)
{
    // Don't want to go negative
  int seconds = lhs.GetSeconds() - rhs.GetSeconds();
  if (seconds < 0)
    seconds = 0;

    // Create a new one and return it
  StopWatch sw(seconds);
  return sw;
}
Test it:
StopWatch sw1(1, 30, 0); // 01:30:00
StopWatch sw2(10, 0, 0); // 10:00:00

  // Subtract smaller from larger
StopWatch sw3 = sw2 - sw1;
sw3.Display(); // 00:08:30

  // Subtract larger from smaller
sw3 = sw1 - sw2;
sw3.Display(); // 00:00:00
Simple. In a nutshell, that's about it. (Like everything in C++, there are lots of little details involved.)


How about something like this:

  // Double the time
StopWatch sw2 = sw1 * 2;
Of course, we'll get this:
error: no match for 'operator*' in 'sw1 * 2'
Fair enough. Another trivial function to implement:
// Multiply: StopWatch * int
StopWatch operator*(const StopWatch& lhs, int rhs)
{
    // Multiply a StopWatch and an integer
  StopWatch sw(lhs.GetSeconds() * rhs);

    // Return the result
  return sw;
}

And the problem is solved. Or is it?

  // Double the time?
StopWatch sw3 = 2 * sw1;

Error message:
error: no match for 'operator*' in '2 * sw1'
note: candidates are: StopWatch operator*(const StopWatch&, int)

Another function:
// Multiply: int * StopWatch
StopWatch operator*(int lhs, const StopWatch& rhs)
{
    // Multiply an integer and a StopWatch
  StopWatch sw(lhs * rhs.GetSeconds());

    // Return the result
  return sw;
}

A better solution (especially if the function is more complex):
// Multiply: int * StopWatch
StopWatch operator*(int lhs, const StopWatch& rhs)
{
    // Simply reverse the operands
  return rhs * lhs;
}
Now, this multiply function will simply call the first multiply function.


How about adding an integer to a StopWatch:

Current implementation (add 2 Stopwatches)New expected behavior
StopWatch operator+(const StopWatch& lhs, 
                    const StopWatch& rhs)
{
    // lhs is a StopWatch
    // rhs is a StopWatch
  StopWatch sw(lhs.GetSeconds() + rhs.GetSeconds());

    // Return the result
  return sw;
}
StopWatch sw1(60); // 00:01:00

  // Add another minute?
StopWatch sw2 = sw1 + 60;
What will the compiler say about this?

Remember these?

1.  3 + 4       ==> evaluates to an int, 7
2.  3.1 + 4.1   ==> evaluates to a double, 7.2
3.  2.4f + 5.1f ==> evaluates to a float, 7.5
4.  3.1 + 4     ==> evaluates to a double, 7.1
All operands must be the same for this to work. The fourth one is really changed to this (by your Friendly Neighborhood™ compiler):
3.1 + (double)4
So the compiler will attempt to do this so it can use the available operator+:
StopWatch sw2 = sw1 + (StopWatch)60;
It's more obvious using a C++-style cast:
StopWatch sw2 = sw1 + (StopWatch)60; // C-style cast
StopWatch sw2 = sw1 + StopWatch(60); // C++-style cast

Note: Any constructor that requires only a single argument is called a conversion constructor and it is called implicitly by the compiler when a conversion needs to be performed.

This conversion process is mentioned in the error message from Microsoft's compiler before we implemented operator+:

g++ error:

error: no match for 'operator+' in 'sw1 + sw2'
MS error:
binary '+' : 'StopWatch' does not define this operator or a conversion to 
             a type acceptable to the predefined operator 
Question: Given these functions below:
// Multiply: StopWatch * int
StopWatch operator*(const StopWatch& lhs, int rhs)
{
    // Multiply a StopWatch and an integer
  StopWatch sw(lhs.GetSeconds() * rhs);

    // Return the result
  return sw;
}

// Multiply: int * StopWatch
StopWatch operator*(int lhs, const StopWatch& rhs)
{
    // Simply reverse the operands
  return rhs * lhs;
}
will the compiler accept the code below? (Why or why not?)
StopWatch sw1(60);
StopWatch sw2(10);

  // Multiply two StopWatches
StopWatch sw3 = sw1 * sw2;
We'll have more to say on this later.

Pointers vs. References:

By now it should be very obvious why we are passing references instead of pointers with these overloaded operator functions. If we used pointers, we'd have to use the address-of operator every time we used them. For example, we'd have to do this:

     StopWatch sw3 = &sw1 * &sw2;

which would be very undesirable (not to mention, illegal, as you can't multiply pointers).

Overloaded Operators as Member Functions

Up to this point, all of our overloading was done with regular functions (not part of a class). We could do this because there was a GetSeconds public method:
StopWatch operator+(const StopWatch& lhs, const StopWatch& rhs)
{
    // Calls public gettor method
  StopWatch sw(lhs.GetSeconds() + rhs.GetSeconds());
  return sw;
}
If this method wasn't available, none of this would work. One solution would be to make member functions: (in StopWatch.h)

class StopWatch
{
  public:
      // Public methods ...

      // Overloaded operators (member functions) Notice the const at the end.
    StopWatch operator+(const StopWatch& rhs) const;
    StopWatch operator-(const StopWatch& rhs) const;
    StopWatch operator*(int rhs) const;

  private:
    int seconds_;
};
There are two things that are different about the member functions compared to the global ones. What are they? (This is very important to realize)

Implementations: (in StopWatch.cpp)

Original global functionsNew member functions
StopWatch operator+(const StopWatch& lhs, const StopWatch& rhs)
{
    // Add the two seconds 
  StopWatch sw(lhs.GetSeconds() + rhs.GetSeconds());
  return sw;
}

StopWatch operator-(const StopWatch& lhs, const StopWatch& rhs)
{
    // Subtract from this object's seconds_ 
    // (Maybe check for negative first?)
  StopWatch sw(lhs.GetSeconds() - rhs.GetSeconds());
  return sw;
}

StopWatch operator*(const StopWatch& lhs, int rhs)
{
    // Multiply lhs seconds_ by rhs
  StopWatch sw(lhs.GetSeconds() * rhs);
  return sw;
}
StopWatch StopWatch::operator+(const StopWatch& rhs) const
{
    // Add seconds to this object's seconds_
  StopWatch sw(seconds_ + rhs.seconds_);
  return sw;
}

StopWatch StopWatch::operator-(const StopWatch& rhs) const
{
    // Subtract from this object's seconds_ 
    // (Maybe check for negative first?)
  StopWatch sw(seconds_ - rhs.seconds_);
  return sw;
}

StopWatch StopWatch::operator*(int rhs) const
{
    // Multiply this object's seconds_ by rhs
  StopWatch sw(seconds_ * rhs);
  return sw;
}
All of these function bodies could actually be just one line of code:

  // Add the two seconds 
return StopWatch(lhs.GetSeconds() + rhs.GetSeconds());

Notice that the methods on the right are accessing the private data from the StopWatch object that was passed in to them. This is allowed because the function (method, member function) is part of the StopWatch class.

All of this code works as before:

"Normal" useFunctional notation
void f1()
{
  StopWatch sw1(30);
  StopWatch sw2(10);

  StopWatch sw3 = sw1 + sw2;
  StopWatch sw4 = sw1 + 20;
  StopWatch sw5 = sw1 - sw2;
  StopWatch sw6 = sw1 - 20;
  StopWatch sw7 = sw1 * 10;

  sw3.Display(); // 00:00:40;
  sw4.Display(); // 00:00:50;
  sw5.Display(); // 00:00:20;
  sw6.Display(); // 00:00:10;
  sw7.Display(); // 00:05:00;
}
void f2()
{
  StopWatch sw1(30);
  StopWatch sw2(10);

    // Functional notation
  StopWatch sw3 = sw1.operator+(sw2);
  sw3.Display(); // 00:00:40

    // Functional notation
  StopWatch sw4 = sw1.operator+(20);
  sw4.Display(); // 00:00:50

    // Functional notation
  StopWatch sw7 = sw1.operator*(10);
  sw5.Display(); // 00:05:00
}
Notice which object the method is invoked on. This:

StopWatch sw3 = sw1 + sw2;
is the same as this:
  // Method is invoked on left object
StopWatch sw3 = sw1.operator+(sw2);
not this:
  // This is NOT how it works.
StopWatch sw3 = sw2.operator+(sw1);

It is not random, it is not arbitrary, it is not which ever object the compiler chooses. It is always the left object (with a binary operator). Always.

What about this now?

StopWatch sw1(30);

  // What about this?
StopWatch sw2 = 2 + sw1;

Non-member functionMember function
StopWatch operator+(const StopWatch& lhs, 
                    const StopWatch& rhs)
{
  StopWatch sw(lhs.GetSeconds() + rhs.GetSeconds());
  return sw;
}
StopWatch StopWatch::operator+(const StopWatch& rhs) const
{
    // Add seconds to this object's seconds
  StopWatch sw(seconds_ + rhs.seconds_);
  return sw;
}

With the global function, the functional notation looks like this:

StopWatch sw2 = operator+(2, sw1); 
which converts to this (using the conversion constructor):
StopWatch sw2 = operator+(StopWatch(2), sw1); 
But with built-in types, there are no member functions, so it can't even attempt this:
StopWatch sw2 = 2.operator+(sw1);
This sometimes makes global functions more useful than member functions.

Note: If the left-hand side of an operator is not a user-defined type (e.g it's a built-in type like int or double) then you can't make the function a member function. It must be a global function.

Overloading operator<<

Although the Display method is nice, we'd like to output the C++ way:
StopWatch sw1(60);

   // Works just fine
sw1.Display();

  // But we'd like to do this
std::cout << sw1;

Error message from that line above.

Time for another overload: (This code is almost identical to the code in Display)

std::ostream& operator<<(std::ostream& os, const StopWatch& sw)
{
    // Get the seconds from the object
  int total_seconds = sw.GetSeconds();

    // Calculate hrs/mins/secs
  int hours, minutes, seconds;
  hours = total_seconds / 3600;
  minutes = (total_seconds - (hours * 3600)) / 60;
  seconds = total_seconds % 60;

    // Print them
  os.fill('0');
  os << std::setw(2) << hours << ':';
  os << std::setw(2) << minutes << ':';
  os << std::setw(2) << seconds << std::endl;

    // Must return the ostream reference
  return os;
}
These are the same now:

  // "Infix" notation
std::cout << sw1;
  // Functional notation (global function)
operator<<(std::cout, sw1);
We can also support this:

StopWatch sw1(60);
StopWatch sw2(100);
StopWatch sw3(200);

  // Output:
  //   00:01:00
  //   00:01:40
  //   00:03:20
std::cout << sw1 << sw2 << sw3;
What if the GetSeconds method was removed (or made private)? Make the method a member function?

This is what the funtional notation looks like for a member function:

  // Functional notation (member function)
std::cout.operator<<(sw1);
See the problem?

We can't make it a member function of the StopWatch class. It would need to be a member of the ostream class, which we cannot modify.

The friend Keyword

Since we can't make the output operator a member of the ostream class, we need a way to allow the overloaded output operator function to access private members of the StopWatch class. We do this by making the overloaded output operator a friend of the StopWatch class:
class StopWatch
{
  public:
      // Other public stuff ...

      // Declare this function a friend so it can access the private members of this class
    friend std::ostream& operator<<(std::ostream& os, const StopWatch& rhs);

  private:
    int seconds_;
};
Implementation
Global overloaded operator (friend function)Old member function
std::ostream& operator<<(std::ostream& os, const StopWatch& sw)
{
  int hours, minutes, seconds;

    // A friend can access private members of sw
  hours = sw.seconds_ / 3600;
  minutes = (sw.seconds_ - (hours * 3600)) / 60;
  seconds = sw.seconds_ % 60;

    // Print them
  os.fill('0');
  os << std::setw(2) << hours << ':';
  os << std::setw(2) << minutes << ':';
  os << std::setw(2) << seconds << std::endl;

    // Must return a reference to the ostream
  return os;
}
void StopWatch::Display() const
{
  int hours, minutes, seconds;

    // Can access private members of this object
  hours = seconds_ / 3600;
  minutes = (seconds_ - (hours * 3600)) / 60;
  seconds = seconds_ % 60;

    // Print them
  std::cout.fill('0');
  std::cout << std::setw(2) << hours << ':';
  std::cout << std::setw(2) << minutes << ':';
  std::cout << std::setw(2) << seconds << std::endl;
  
  
  
}

Why do we return an ostream reference? To support this syntax:

int i, j, k;

// Set i, j, k

cout << i << j << k << endl;
The << operator associates left to right. This is why operator<< must return an ostream object. The compiler really sees the above as:
int i, j, k;

// Set i, j, k

(((cout << i) << j) << k) << endl;
((     cout   << j) << k) << endl;
(            cout   << k) << endl;
                   cout   << endl;      
Note:

Just like in the Real World™, friendship is a one-way street: "I may be your friend, but that doesn't make you my friend." Friendship is granted from one entity to another. With operator<<, the StopWatch class is granting friendship to the operator (the operator is NOT granting friendship to the StopWatch class). If you need it to work both ways, then both entities must grant the friendship.

Also, granting friendship is an advanced technique and should be limited to operator<< until you have a very good understanding of C++ class design. In other words, it should be used as a last resort.

Automatic Conversions and User-Defined Types

Recall that a single-argument constructor is called a conversion constructor because it "converts" its argument into an object of the class type.

void f1()
{
  StopWatch sw1; // Default constructor

  sw1 = 60; // same as sw1 = (StopWatch)60;
            // same as sw1 = StopWatch(60);
}
Another example:
Function expects StopWatchCall the function
void fooSW(const StopWatch& sw)
{
  // Do something with sw
  sw.Display();
}
StopWatch sw1(60);
int time = 90;

  // Pass StopWatch
fooSW(sw1);

  // Convert int to StopWatch
fooSW(time);
Both examples convert the integer to a StopWatch automatically and silently. However, the opposite isn't the same thing:

void f1()
{
  StopWatch sw1;

  int seconds1 = sw1;       // Error, how do you convert StopWatch to int?
  int seconds2 = (int) sw1; // Error, how do you convert StopWatch to int?
  int seconds3 = int(sw1);  // Error, how do you convert StopWatch to int?
}
Microsoft:
'initializing' : cannot convert from 'StopWatch' to 'int'
'type cast' : cannot convert from 'StopWatch' to 'int'
GNU:
error: cannot convert 'StopWatch' to 'int' in initialization
error: 'class StopWatch' used where a 'int' was expected
Q: What to do?
A: Same as always, create YAF. (Yet Another Function)

We'll call the function ToInt because it will convert a StopWatch into an int:
DeclarationImplementation
class StopWatch
{
  public:
      // Other public methods ...

      // Convert to int (explicit)
    int ToInt() const;

  private:
    int seconds_;
};
int StopWatch::ToInt() const
{
  return seconds_;
}
Sample usage:

void f2()
{
  StopWatch sw1(60);

    // Convert to an int explicitly
  int seconds = sw1.ToInt();
  std::cout << seconds << std::endl;
}
If we want the compiler to perform the conversions (casts) automatically, then we have to give the function a special name. (Not unlike overloaded operator functions.)

Operator precedence chart for C++
DeclarationImplementation
class StopWatch
{
  public:
      // Other public methods

      // Conversion function/operator (implicit)
    operator int() const;

  private:
    int seconds_;
};
StopWatch::operator int() const
{
  return seconds_;
}

Sample usage:

void f3()
{
  StopWatch sw1(60);

    // Convert to an int implicitly
  int seconds = sw1;
  std::cout << seconds << std::endl;
}
General form of the conversion operator:
operator type() const
Depending on the direction of the conversion:
    Built-in type ===> Conversion constructor ===> User-defined type
User-defined type ===> Conversion operator    ===> Built-in type
For example:
           60 ===> Conversion constructor ===> StopWatch(60)
StopWatch(60) ===> Conversion operator    ===> 60
You must be careful when allowing the compiler to perform automatic conversions for you. These conversions will take place silently and may lead to unintended behavoir. Here's an example from the textbook:

int array[10];
StopWatch temp1(60);

// Other code...

int temp2 = 0;

// Other code...

int value = array[temp1];
std::cout << value << std::endl;

int swval = temp1;
std::cout << swval << std::endl;
Output:
2281060
60
If we remove the implicit conversion function and only have the explicit one, this error would be detected by the compiler:

error: no match for 'operator[]' in 'array[temp1]'
error: cannot convert `StopWatch' to `int' in initialization
We can still convert a StopWatch to an integer explicitly:
int array[10];
StopWatch temp1(60);

// Other code...

int temp2 = 0;

// Other code...

int value = array[temp2];  // correct
std::cout << value << std::endl;

int swval = temp1.ToInt(); // explicit
std::cout << swval << std::endl;
We can also instruct the compiler not to automatically convert an integer to a StopWatch (conversion constructor):
class StopWatch
{
  public:
      // Constructors
    StopWatch();

      // Suppress automatic conversions
    explicit StopWatch(int seconds);

    StopWatch(int hours, int minutes, int seconds);

    // Other public methods

  private:
    int seconds_;
};

Given this function from before:

void fooSW(const StopWatch& sw)
{
  // Do something with sw
  sw.Display();
}

IllegalLegal
int time = 90;

  // Error: No implicit conversions
fooSW(time);
int time = 90;

  // OK: Explicit conversion
fooSW(StopWatch(time));

C++11 has added the ability to mark conversion functions explicit (like conversion constructors). This means you can use the C++-style of explicit casting without having the problem of the compiler doing it unintentionally.

For example, the StopWatch class can make the conversion function explicit:
  // Conversion function/operator (explicit)
explicit operator int() const;
Sample usage:
StopWatch sw1(60);
  
int seconds = sw1;      // Error: Can't convert to an int implicitly
int seconds = int(sw1); // OK: Explicit conversion

Overloading Operators with Side-effects

Up to this point:
DeclarationsImplementations
class StopWatch
{
  public:
      // Overload for: sw1 + sw2
    StopWatch operator+(const StopWatch& rhs) const;

      // Overload for: sw1 += sw2
    StopWatch& operator+=(const StopWatch& rhs);

      // Other public methods ...

  private:
    int seconds_;
};
StopWatch StopWatch::operator+(const StopWatch& rhs) const
{
    // Create a new object from both operands
  StopWatch sw(seconds_ + rhs.seconds_);
  return sw;
}

StopWatch& StopWatch::operator+=(const StopWatch& rhs)
{
    // Modify this object directly
  seconds_ += rhs.seconds_;
  return *this;
}
Things to notice:

Challenge Question: How will you overload the pre/post increment operators to support this code?

void foo()
{
  StopWatch sw1(60);

    // Pre increment 
    // afterwards sw2 == sw1
  StopWatch sw2 = ++sw1;
  std::cout << sw1; // 00:01:01
  std::cout << sw2; // 00:01:01

    // Post increment 
    // afterwards sw2 != sw1
  sw2 = sw1++;
  std::cout << sw1; // 00:01:02
  std::cout << sw2; // 00:01:01

    // Fails to compile. Same as this: ++(sw1++) 
  sw2 = ++sw1++;

    // Pre and post increment 
    // afterwards sw2 != sw1
  sw2 = (++sw1)++;
  std::cout << sw1; // 00:01:04
  std::cout << sw2; // 00:01:03
}
Operator precedence chart for C++ will help you understand why one of the expressions fails to compile.

Member, Friend, or Non-member?

In most cases, you have three choices when overloading operators: 1) member, 2) friend, 3) non-member/non-friend (global or in a namespace)
  1. Member function: Implement the overloaded operator in the class.
    StopWatch StopWatch::operator*(int factor) const
    {
      StopWatch sum(seconds_ * factor);
      return sum;
    }
    
    1. Binary operators will take one operand; the left operand is implicit via this.
    2. Unary operators will not take any parameters.
    3. Mark the function as const, if it doesn't modify the object.
    4. Remember: Member functions have a hidden "this" parameter, which gives access to the private data.

  2. Friend function: Declare a friend function to overload the operator and implement it outside of the class.
    // Declaration (in class StopWatch) requires the friend keyword.
    friend StopWatch operator*(int factor, const StopWatch &sw);
    
    // Definition (outside of class StopWatch) must NOT have the friend keyword.
    StopWatch operator*(int factor, const StopWatch &sw)
    {
      StopWatch sum(sw.seconds_ * factor);
      return sum;
    }
    

    1. Binary operators will have two operands.
    2. Unary operators will have one operand.
    3. You cannot mark the function with const since only member functions can be marked as const.
    4. Remember: A friend function has access to the private members of the parameters.

  3. Non-member, non-friend function #1: Implement a non-member, non-friend function to overload the operator.
    StopWatch operator*(int factor, const StopWatch &sw)
    {
      StopWatch sum(sw.GetSeconds() * factor);
      return sum;
    }
    

    1. The function can only access the public methods of the class.
    2. Binary operators will have two operands.
    3. Unary operators will have one operand.
    4. You cannot mark the function with const since only member functions can be marked as const.
    5. Must have a public get method or some way to access the private data.

  4. Non-member, non-friend function #2: Implement a non-member, non-friend function to overload the operator that will call an existing member function. This technique is used a lot with commutative operators and built-in types (e.g. integer).
    StopWatch operator*(int factor, const StopWatch &sw)
    {
      // the compiler sees it like this: sw.operator*(factor)
      return sw * factor; 
    }
    
    1. We can actually take a "shortcut" and just create a "regular" overloaded operator function in certain situations.
    2. This assumes that the operator is commutative, e.g. a * b is the same as b * a.

Study these four functions above and be sure you understand exactly how each one is different and why. Also make sure you know why some functions have 1 parameter and some have 2 parameters. Unary operators will have 0 or 1 parameter(s), depending on whether they are members or not.

Restrictions on Operator Overloading

  1. At least one operand must be a user-defined type. This means you can't overload the built-in types.
      // Compiler error: Cannot overload built-in operators for int
    int operator+(int left, int right);
    
  2. You can't violate the C++ syntax rules for the operator you wish to overload. This means that if you overload the modulus operator (%), you can't use it with just a single operand (modulus is a binary operator):
    int i;
    StopWatch sw;
    
    % i;  // % requires 2 integer operands
    % sw; // if overloaded for StopWatch, % would require 2 operands
    This also means that you can't change the precedence or associativity of an operator. (What a nightmare that would be!)

  3. You can't create new operator symbols. As mentioned above, the '@' character (at sign) isn't a C++ operator for the built-in types, so you can't define it for a user-defined type. (Like #2, it would require a lot of thought/work.)

  4. You cannot overload the following operators (from Stroustrup's site):
    ::                   Scope resolution operator
    .*                   Pointer-to-member operator
    .                    Membership operator
    ?:                   Conditional operator
    sizeof               The sizeof operator
    typeid               A RTTI operator
    const_cast           A type cast operator 
    dynamic_cast         A type cast operator
    reinterpret_cast     A type cast operator
    static_cast          A type cast operator
    
  5. Most operators can be overloaded using either member or non-member functions. The following operators can only be overloaded with member functions.
    =   Assignment operator  
    ()  Function call operator
    []  Subscripting operator
    ->  Class member access by pointer operator
    
  6. That leaves these operators that can be overloaded:
    +      -      *      /      %      ^      &      |
    ~=     !      =      <      >      +=     -=     *=
    /=     %=     ^=     &=     |=     <<     >>     >>=
    <<=    ==     !=     <=     >=     &&     ||     ++
    --     ,      ->*    ->     ()     []     new    delete
    new []  delete []
    
Notes:

Member Initialization List

A simple Point class that uses the constructor to give values to the private data:

DeclarationImplementation
class Point
{
  public:
    Point(int x = 0, int y = 0);

  private:
    int x_;
    int y_;
};
Point::Point(int x, int y)
{
  x_ = x;  // assignment
  y_ = y;  // assignment
}
Constructors can use an initializer list to initialize data members:

Point::Point(int x, int y) : x_(x), y_(y) // initialization

{
}
The result for this example is the same as the first constructor above.

Look at the class below. Can you see what is wrong with the constructor? (The compiler will give errors.)

Class definitionConstructor implementation
class Foo
{
  public:
    Foo(int x, int y, int& z);

  private:
    int x_;
    const int y_;
    int& z_;
};
Foo::Foo(int x, int y, int& z)
{
  x_ = x;  // assignment
  y_ = y;  // assignment
  z_ = z;  // assignment
}
Simple usage:

void f20()
{
  int a = 1, b = 2, c = 3;
  Foo foo(a, b, c); // 1,2,3
}


These are the error messages given:
error: uninitialized member 'Foo::y_' with 'const' type 'const int'
error: uninitialized reference member 'Foo::z_'
error: assignment of read-only data-member 'Foo::y_'
Constants and references must be initialized via the initializer list (otherwise, it's an assignment, which is illegal). The references notes.
Foo::Foo(int x, int y, int& z) : y_(y), z_(z)  // initialization
{
  x_ = x;  // assignment
}
Some people prefer to initialize all members in the list:
Foo::Foo(int x, int y, int& z) : y_(y), z_(z), x_(x)  // initialization
{
}
Note that g++ will give you a warning with the above code:
warning: `Foo::z_' will be initialized after
warning:   `int Foo::x_'
warning:   when initialized here
The proper way:
Foo::Foo(int x, int y, int& z) : x_(x), y_(y), z_(z)
{
}
Notes:
Using -Weffc++ (from the g++ man page)