Operator Overloading

Steven Zeil

Last modified: Jul 16, 2014

Contents:
1. Operators
1.1 Operators are Functions
2. I/O Operators
3. Comparison Operators
3.1 Equality Operators
3.2 Less-Than Operators

1. Operators

Lots of Operators

In C++, almost every operator in the language can be declared for our own classes.

1.1 Operators are Functions


Examples: operators as functions


Declaring Operators

Understanding that shorthand, we can declare our own operators by giving the appropriate operator function name:

class BidCollection {
      ⋮
  void addInTimeOrder (const Bid& value);
  //  Adds this bid into a position such that 
  //   all bids are ordered by the time the bid was placed
  //Pre: getSize() < getMaxSize()

  void operator+= (const Bid& value)  
           {addInTimeOrder(value);}

Then

bids.addInTimeOrder(b);

and

bids += b;

would do the same thing


Keep it Natural


Most Commonly Overloaded Operators

There are 3 groups of operators that are very commonly overloaded

2. I/O Operators

Perhaps the most commonly declared operators


Providing the Output Operator

For example, our Bid class already looks like:

class Bid {
  ⋮
public:
  Bid (std::string bidder, double amt, 
       std::string item, Time placedAt);

  Bid();

  // Access to attribute
  std::string getBidder() const {return bidderName;}
  double getAmount() const {return amount;}
  std::string getItem() const {return itemName;}
  Time  getTimePlacedAt() const {return bidPlacedAt;}

  // Print a bid
  void print (std::ostream& out) const;
};

All we need to do is add:

inline
std::ostream& operator<< (std::ostream& out, const Bid& b)
{
  b.print(out);
  return out;
}

Lots of Ops

The whole program, with output operators:

auctionOut/bids.h

and

auctionOut/bids.cpp

auctionOut/bidders.h

and

auctionOut/bidders.cpp

auctionOut/items.h

and

auctionOut/items.cpp

auctionOut/time.h

and

auctionOut/time.cpp

auctionOut/bidcollection.h

       and 

auctionOut/bidcollection.cpp

auctionOut/biddercollection.h

       and 

auctionOut/biddercollection.cpp

auctionOut/itemcollection.h

       and 

auctionOut/itemcollection.cpp


Debugging with the Output Op

Note how much cleaner the debugging output statements look.

Instead of

  if (bid.getAmount() <= bidder.getBalance())
    {
      highestBidSoFar = bid.getAmount();
      winningBidderSoFar = bid.getBidder();
      cerr << "Best bid so far is ";
      bid.print (cerr);
      cerr << " from ";
      bidder.print (cerr);
      cerr << endl;
    }

we now can write

  if (bid.getAmount() <= bidder.getBalance())
    {
      highestBidSoFar = bid.getAmount();
      winningBidderSoFar = bid.getBidder();
      cerr << "Best bid so far is "
           << bid << " from "
           << bidder << endl;
    }

Return Values

The << operator must return the stream it is applied to.


Fine Points

3. Comparison Operators

Almost every class should provide == and < operators.

auctionComp/arrayUtils.h


Comparison Operators for Time

time.h

time.cpp

3.1 Equality Operators


Equality Operators for Compound Data


Common: Compare Every Member

For ADTs with multiple data members, we often expect every “significant” data member to match. E.g.,

class Bid {

  std::string bidderName;
  double amount;
  std::string itemName;
  Time bidPlacedAt;
    ⋮

can be compared like this

bool Bid::operator== (const Bid& b) const
{
  return bidderName == b.bidderName
    && amount == b.amount
    && itemName == b.itemName
    && bidPlacedAt == b.bidPlacedAt;
}

Highly Variable Data

Fields that change frequently may or may not be significant:

bool Bidder::operator== (const Bidder& b) const
{
  return name == b.name;  // ignore balance
}


Equality for ADTs with Arrays

bool BidCollection::operator== (const BidCollection& bc) const
{
  if (size == bc.size)                  ➊
    {
      for (int i = 0; i < size; ++i)         ➋
        if (!(elements[i] == ➌bc.elements[i])) 
          return false;
      return true;
    }
  else
    return false;
}

3.2 Less-Than Operators

< is not always “less than”


Less-than on Compound Data


Extending Lexicographic Order to Non-Strings

Compare members until we find two that are not equal:

bool Bid::operator< (const Bid& b) const
{
  if (bidPlacedAt < b.bidPlacedAt)
    return true;
  else if (b.bidPlacedAt < bidPlacedAt)
    return false;

  if (bidderName < b.bidderName)
    return true;
  else if (b.bidderName < bidderName)
    return false;

  if (itemName < b.itemName)
    return true;
  else if (b.itemName < itemName)
    return false;

  if (amount < b.amount)
    return true;
  else if (b.amount < amount)
    return false;

  return false;
}

Less-than for ADTs with Arrays

We extend the same idea to arrays of data:

bool BidCollection::operator< (const BidCollection& bc) const
{
  if (size == bc.size)                 ➊
    {
      for (int i = 0; i < size; ++i)   ➋
        {
          if (elements[i] < bc.elements[i])   ➌
            return true;
          else if (bc.elements[i] < elements[i])  ➌
            return false;
          // else keep going
        }

      // All of the elements are equal
      return false;                               ➍
    }
  else
    return size < bc.size;   ➊
}


Adding Both == and <

We often have choices in how to implement these two.


Why Just Two?

timeComp.h


Taking Advantage of the Relational Ops

If we make a habit of providing these, we can often use “canned” versions of our algorithms instead of needing to write specialized versions.

For example, given:

auctionComp/arrayUtils.h