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
#ifndef BIDS_H
#define BIDS_H

#include <iostream>
#include <string>
#include "time.h"


//
//  Bids Received During Auction
//

class Bid {

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

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;
};

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



#endif

and

auctionOut/bids.cpp
#include <string>
#include <fstream>

using namespace std;

//
//  Bids Received During Auction
//

#include "bids.h"


Bid::Bid (std::string bidder, double amt, 
     std::string item, Time placedAt)
  : bidderName(bidder), amount(amt),
    itemName(item), bidPlacedAt(placedAt)
{
}

Bid::Bid ()
  : amount(0.0)
{
}


// Print a bid
void Bid::print (std::ostream& out) const
{
  out << "[" << bidderName << " " << amount
      << " " << itemName << " "
      << bidPlacedAt << "]";
}

auctionOut/bidders.h
#ifndef BIDDERS_H
#define BIDDERS_H

#include <iostream>
#include <string>

//
//  Bidders Registered for auction
//


class Bidder {
  // describes someone registered to participate in an auction
  std::string name;
  double balance;

public:
  
  Bidder();
  Bidder (std::string theName, double theBalance);

  // Access to attributes
  std::string getName() const {return name;}

  double getBalance() const {return balance;}
  void setBalance (double newBal) {balance = newBal;}

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

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

#endif

and

auctionOut/bidders.cpp
#include <string>
#include <fstream>
#include <iostream>
//
//  Bidders Registered for auction
//

#include "bidders.h"

using namespace std;



Bidder::Bidder()
{
  name = "";
  balance = 0.0;
}

Bidder::Bidder (std::string theName, double theBalance)
{
  name = theName;
  balance = theBalance;
}


// print a bidder
void Bidder::print (std::ostream& out) const
{
  out << "[" << name << " " << balance << "]";
}

auctionOut/items.h
#ifndef ITEMS_H
#define ITEMS_H

#include <iostream>
#include <string>

#include "time.h"

//
//  Items up for auction
//


class Item {
  std::string name;
  double reservedPrice;
  Time auctionEndsAt;

public:
  Item();
  Item (std::string itemName, double reserve, Time auctionEnd);

  // Access to attribute

  std::string getName() const {return name;}
  
  double getReservedPrice() const {return reservedPrice;}

  Time getAuctionEndTime() const {return auctionEndsAt;}


  /**
   * Read one item from the indicated file
   */
  void read (std::istream& in);

  // Print an item
  void print (std::ostream& out) const;
};

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

#endif

and

auctionOut/items.cpp
#include <iostream>
#include <fstream>

#include "items.h"

//
//  Items up for auction
//

using namespace std;




Item::Item()
  : reservedPrice(0.0)
{
}

Item::Item (std::string itemName, double reserve, Time auctionEnd)
  : name(itemName), reservedPrice(reserve), auctionEndsAt(auctionEnd)
{
}


/**
 * Read one item from the indicated file
 */
void Item::read (istream& in)
{
  in >> reservedPrice;
  auctionEndsAt.read (in);

  // Reading the item name.
  char c;
  in >> c; // Skips blanks and reads first character of the name
  string line;
  getline (in, line); // Read the rest of the line
  name = string(1,c) + line;
}


// print a bidder
void Item::print (std::ostream& out) const
{
  out << "[" << name << " " << reservedPrice << " "
      << auctionEndsAt << "]";
}

auctionOut/time.h
#ifndef TIMES_H
#define TIMES_H

#include <iostream>

/**
 * Times in this program are represented by three integers: H, M, & S, representing
 * the hours, minutes, and seconds, respecitvely.
 */


struct Time {
  // Create time objects
  Time(); // midnight
  Time (int h, int m, int s);

  // Access to attributes
  int getHours() const;
  int getMinutes() const;
  int getSeconds() const;

  // Calculations with time
  void add (Time delta);
  Time difference (Time fromTime);
  
  /**
   * Read a time (in the format HH:MM:SS) after skipping any
   * prior whitepace.
   */
  void read (std::istream& in);


  /**
   * Print a time in the format HH:MM:SS (two digits each)
   */
  void print (std::ostream& out) const;

  /**
   *  Compare two times. Return true iff time1 is earlier than or equal to
   *  time2
   */
  bool noLaterThan(const Time& time2);


  /**
   *  Compare two times. Return true iff time1 is equal to
   *  time2
   *
   */
  bool equalTo(const Time& time2);

  // From here on is hidden
  int secondsSinceMidnight;
};

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

inline
std::istream& operator>> (std::istream& in, Time& t)
{
  t.read(in);
  return in;
}

#endif // TIMES_H

and

auctionOut/time.cpp
#include "time.h"
#include <iomanip>

using namespace std;

/**
 * Times in this program are represented by three integers: H, M, & S, representing
 * the hours, minutes, and seconds, respecitvely.
 */



  // Create time objects
Time::Time() // midnight
{
  secondsSinceMidnight = 0;
}


Time::Time (int h, int m, int s)
{
  secondsSinceMidnight = s + 60 * m + 3600*h;
}

  // Access to attributes
int Time::getHours() const
{
  return secondsSinceMidnight / 3600;
}

int Time::getMinutes() const
{
  return (secondsSinceMidnight % 3600) / 60;
}

int Time::getSeconds() const
{
  return secondsSinceMidnight % 60;
}

// Calculations with time
void Time::add (Time delta)
{
  secondsSinceMidnight += delta.secondsSinceMidnight;
}
      

Time Time::difference (Time fromTime)
{
  Time diff;
  diff.secondsSinceMidnight = 
    secondsSinceMidnight - fromTime.secondsSinceMidnight;
}
  
  
/**
 * Read a time (in the format HH:MM:SS) after skipping any
 * prior whitepace.
 */
void Time::read (std::istream& in)
{
  char c;
  int hours, minutes, seconds;
  in >> hours >> c >> minutes >> c >> seconds;
  Time t (hours, minutes, seconds);
  secondsSinceMidnight = t.secondsSinceMidnight;
}


/**
 * Print a time in the format HH:MM:SS (two digits each)
 */
void Time::print (std::ostream& out) const
{
  out << setfill('0') << setw(2) << getHours() << ':' 
      << setfill('0') << setw(2) << getMinutes() << ':' 
      << setfill('0') << setw(2) << getSeconds();
}



/**
 *  Compare two times. Return true iff time1 is earlier than or equal to
 *  time2
 */
bool Time::noLaterThan(const Time& time2)
{
  return secondsSinceMidnight <= time2.secondsSinceMidnight;
}



/**
 *  Compare two times. Return true iff time1 is equal to
 *  time2
 *
 */
bool Time::equalTo(const Time& time2)
{
  return secondsSinceMidnight == time2.secondsSinceMidnight;
}

auctionOut/bidcollection.h
#ifndef BIDCOLLECTION_H
#define BIDCOLLECTION_H

#include "bids.h"
#include <iostream>

class BidCollection {

  int MaxSize;
  int size;
  Bid* elements; // array of bids 

public:

  /**
   * Create a collection capable of holding the indicated number of bids
   */
  BidCollection (int MaxBids = 1000);

  ~BidCollection ();

  

  // Access to attributes
  int getMaxSize() const {return MaxSize;}

  int getSize() const {return size;}



  // Access to individual elements

  const Bid& get(int index) const {return elements[index];}



  // Collection operations


  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 remove (int index);
  // Remove the bid at the indicated position
  //Pre: 0 <= index < getSize()


  /**
   * Read all bids from the indicated file
   */
  void readBids (std::string fileName);

  // Print the collection
  void print (std::ostream& out) const;
};


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

#endif

       and 

auctionOut/bidcollection.cpp
#include "bidcollection.h"
#include "arrayUtils.h"
#include <fstream>

using namespace std;


/**
 * Create a collection capable of holding the indicated number of bids
 */
BidCollection::BidCollection (int MaxBids)
  : MaxSize(MaxBids), size(0)
{
  elements = new Bid [MaxSize];
}

BidCollection::~BidCollection ()
{
  delete [] elements;
}



// Collection operations


void BidCollection::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()
{
  // Make room for the insertion
  int toBeMoved = size - 1;
  while (toBeMoved >= 0 && 
     value.getTimePlacedAt().noLaterThan(elements[toBeMoved].getTimePlacedAt())) {
    elements[toBeMoved+1] = elements[toBeMoved];
    --toBeMoved;
  }
  // Insert the new value
  elements[toBeMoved+1] = value;
  ++size;  
}


void BidCollection::remove (int index)
// Remove the bid at the indicated position
//Pre: 0 <= index < getSize()
{
  removeElement (elements, size, index);
}

/**
 * Read all bids from the indicated file
 */
void BidCollection::readBids (std::string fileName)
{
  size = 0;
  ifstream in (fileName.c_str());
  int nBids;
  in >> nBids;
  for (int i = 0; i < nBids; ++i)
    {
      char c;
      
      string bidderName;
      double amount;
      in >> bidderName >> amount;
      
      Time bidPlacedAt;
      bidPlacedAt.read (in);
      
      string word, line;
      in >> word; // First word of itemName
      getline (in, line); // rest of item name
      
      string itemName = word + line;
      addInTimeOrder (Bid (bidderName, amount, itemName, bidPlacedAt));
    }
}


// Print the collection
void BidCollection::print (std::ostream& out) const
{
  out << size << "/" << MaxSize << "{";
  for (int i = 0; i < size; ++i)
    {
      out << "  ";
      elements[i].print (out);
      out << "\n";
    }
  out << "}";
}

auctionOut/biddercollection.h
#ifndef BIDDERCOLLECTION_H
#define BIDDERCOLLECTION_H

#include "bidders.h"
#include <iostream>

//
//  Bidders Registered for auction
//


class BidderCollection {

  int MaxSize;
  int size;
  Bidder* elements; // array of items 

public:

  /**
   * Create a collection capable of holding the indicated number of items
   */
  BidderCollection (int MaxBidders = 1000);

  ~BidderCollection ();

  

  // Access to attributes
  int getMaxSize() const {return MaxSize;}

  int getSize() const {return size;}



  // Access to individual elements

  Bidder& get(int index) {return elements[index];}



  // Collection operations


  int add (const Bidder& value);
  //  Adds this bidder to the collection at an unspecified position.
  //  Returns the position where added.
  //Pre: getSize() < getMaxSize()



  void remove (int index);
  // Remove the item at the indicated position
  //Pre: 0 <= index < getSize()


  int findBidder (std::string name) const;
  // Returns the position where a bidde mathcing the given
  // name can be found, or getSize() if no bidder with that name exists.



  /**
   * Read all items from the indicated file
   */
  void readBidders (std::string fileName);

  // Print the collection
  void print (std::ostream& out) const;
};

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

#endif




       and 

auctionOut/biddercollection.cpp
#include <iostream>
#include "arrayUtils.h"
#include <fstream>

#include "biddercollection.h"

using namespace std;


/**
 * Create a collection capable of holding the indicated number of items
 */
BidderCollection::BidderCollection (int MaxBidders)
  : MaxSize(MaxBidders), size(0)
{
  elements = new Bidder [MaxSize];
}

BidderCollection::~BidderCollection ()
{
  delete [] elements;
}



// Collection operations


int BidderCollection::add (const Bidder& value)
//  Adds this bidder
//Pre: getSize() < getMaxSize()
{
  addToEnd (elements, size, value);
  return size - 1;
}


void BidderCollection::remove (int index)
// Remove the bidder at the indicated position
//Pre: 0 <= index < getSize()
{
  removeElement (elements, size, index);
}




/**
 * Read all bidders from the indicated file
 */
void BidderCollection::readBidders (std::string fileName)
{
    size = 0;
    ifstream in (fileName.c_str());
    int nBidders;
    in >> nBidders;
    for (int i = 0; i < nBidders && i < MaxSize; ++i)
    {
      string nme;
      double bal;
      in >> nme >> bal;
      Bidder bidder (nme, bal);;
      add (bidder);
    }
}


/**
 * Find the index of the bidder with the given name. If no such bidder exists,
 * return nBidders.
 */
int BidderCollection::findBidder (std::string name) const
{
  int found = size;
  for (int i = 0; i < size && found == size; ++i)
    {
      if (name == elements[i].getName())
    found = i;
    }
  return found;
}


// Print the collection
void BidderCollection::print (std::ostream& out) const
{
  out << size << "/" << MaxSize << "{";
  for (int i = 0; i < size; ++i)
    {
      out << "  ";
      elements[i].print (out);
      out << "\n";
    }
  out << "}";
}

auctionOut/itemcollection.h
#ifndef ITEMCOLLECTION_H
#define ITEMCOLLECTION_H

#include "items.h"
#include <iostream>

class ItemCollection {

  int MaxSize;
  int size;
  Item* elements; // array of items 

public:

  /**
   * Create a collection capable of holding the indicated number of items
   */
  ItemCollection (int MaxItems = 1000);

  ~ItemCollection ();

  

  // Access to attributes
  int getMaxSize() const {return MaxSize;}

  int getSize() const {return size;}



  // Access to individual elements

  const Item& get(int index) const {return elements[index];}



  // Collection operations


  void addInTimeOrder (const Item& value);
  //  Adds this item into a position such that 
  //   all items are ordered by the time at which the auction for the
  //   item ends.
  //Pre: getSize() < getMaxSize()



  void remove (int index);
  // Remove the item at the indicated position
  //Pre: 0 <= index < getSize()


  /**
   * Read all items from the indicated file
   */
  void readItems (std::string fileName);

  // Print the collection
  void print (std::ostream& out) const;
};


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

#endif



       and 

auctionOut/itemcollection.cpp
#include <iostream>
#include "arrayUtils.h"
#include <fstream>

#include "itemcollection.h"

//
//  Items up for auction
//

using namespace std;


/**
 * Create a collection capable of holding the indicated number of items
 */
ItemCollection::ItemCollection (int MaxItems)
  : MaxSize(MaxItems), size(0)
{
  elements = new Item [MaxSize];
}

ItemCollection::~ItemCollection ()
{
  delete [] elements;
}



// Collection operations


void ItemCollection::addInTimeOrder (const Item& value)
//  Adds this item into a position such that 
//   all items are ordered by the time the item' auction ends
//Pre: getSize() < getMaxSize()
{
  // Make room for the insertion
  int toBeMoved = size - 1;
  while (toBeMoved >= 0 && 
     value.getAuctionEndTime().noLaterThan(elements[toBeMoved].getAuctionEndTime())) {
    elements[toBeMoved+1] = elements[toBeMoved];
    --toBeMoved;
  }
  // Insert the new value
  elements[toBeMoved+1] = value;
  ++size;  
}


void ItemCollection::remove (int index)
// Remove the item at the indicated position
//Pre: 0 <= index < getSize()
{
  removeElement (elements, size, index);
}



/**
 * Read all items from the indicated file
 */
void ItemCollection::readItems (std::string fileName)
{
  size = 0;
  ifstream in (fileName.c_str());
  int nItems;
  in >> nItems;
  for (int i = 0; i < nItems; ++i)
    {
      Item item;
      item.read (in);
      addInTimeOrder (item);
    }
}

// Print the collection
void ItemCollection::print (std::ostream& out) const
{
  out << size << "/" << MaxSize << "{";
  for (int i = 0; i < size; ++i)
    {
      out << "  ";
      elements[i].print (out);
      out << "\n";
    }
  out << "}";
}



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
#ifndef ARRAYUTILS_H
#define ARRAYUTILS_H



//  Add to the end
//  - Assumes that we have a separate integer (size) indicating how
//     many elements are in the array
//  - and that the "true" size of the array is at least one larger 
//      than the current value of that counter
template <typename T>
void addToEnd (T* array, int& size, T value)
{
   array[size] = value;
   ++size;
}



// Add value into array[index], shifting all elements already in positions
//    index..size-1 up one, to make room.
//  - Assumes that we have a separate integer (size) indicating how
//     many elements are in the array
//  - and that the "true" size of the array is at least one larger 
//      than the current value of that counter

template <typename T>
void addElement (T* array, int& size, int index, T value)
{
  // Make room for the insertion
  int toBeMoved = size - 1;
  while (toBeMoved >= index) {
    array[toBeMoved+1] = array[toBeMoved];
    --toBeMoved;
  }
  // Insert the new value
  array[index] = value;
  ++size;
}


// Assume the elements of the array are already in order
// Find the position where value could be added to keep
//    everything in order, and insert it there.
// Return the position where it was inserted
//  - Assumes that we have a separate integer (size) indicating how
//     many elements are in the array
//  - and that the "true" size of the array is at least one larger 
//      than the current value of that counter

template <typename T>
int addInOrder (T* array, int& size, T value)
{
  // Make room for the insertion
  int toBeMoved = size - 1;
  while (toBeMoved >= 0 && value < array[toBeMoved]) {
    array[toBeMoved+1] = array[toBeMoved];
    --toBeMoved;
  }
  // Insert the new value
  array[toBeMoved+1] = value;
  ++size;
  return toBeMoved+1;
}


// Search an array for a given value, returning the index where 
//    found or -1 if not found.
template <typename T>
int seqSearch(const T list[], int listLength, T searchItem)
{
    int loc;

    for (loc = 0; loc < listLength; loc++)
        if (list[loc] == searchItem)
            return loc;

    return -1;
}


// Search an ordered array for a given value, returning the index where 
//    found or -1 if not found.
template <typename T>
int seqOrderedSearch(const T list[], int listLength, T searchItem)
{
    int loc = 0;

    while (loc < listLength && list[loc] < searchItem)
      {
       ++loc;
      }
    if (loc < listLength && list[loc] == searchItem)
       return loc;
    else
       return -1;
}


// Removes an element from the indicated position in the array, moving
// all elements in higher positions down one to fill in the gap.
template <typename T>
void removeElement (T* array, int& size, int index)
{
  int toBeMoved = index + 1;
  while (toBeMoved < size) {
    array[toBeMoved] = array[toBeMoved+1];
    ++toBeMoved;
  }
  --size;
}



// Search an ordered array for a given value, returning the index where 
//    found or -1 if not found.
template <typename T>
int binarySearch(const T list[], int listLength, T searchItem)
{
    int first = 0;
    int last = listLength - 1;
    int mid;

    bool found = false;

    while (first <= last && !found)
    {
        mid = (first + last) / 2;

        if (list[mid] == searchItem)
            found = true;
        else 
            if (searchItem < list[mid])
                last = mid - 1;
            else
                first = mid + 1;
    }

    if (found) 
        return mid;
    else
        return -1;
}





#endif


Comparison Operators for Time

time.h
#ifndef TIMES_H
#define TIMES_H

#include <iostream>

/**
 * Times in this program are represented by three integers: H, M, & S, representing
 * the hours, minutes, and seconds, respecitvely.
 */


class Time {
public:
  // Create time objects
  Time(); // midnight
  Time (int h, int m, int s);

  // Access to attributes
  int getHours() const;
  int getMinutes() const;
  int getSeconds() const;

  // Calculations with time
  void add (Time delta);
  Time difference (Time fromTime);
  
  /**
   * Read a time (in the format HH:MM:SS) after skipping any
   * prior whitepace.
   */
  void read (std::istream& in);


  /**
   * Print a time in the format HH:MM:SS (two digits each)
   */
  void print (std::ostream& out) const;



  // Comparison operators
  bool operator== (const Time&) const;
  bool operator< (const Time&) const;

private:
  // From here on is hidden
  int secondsSinceMidnight;
};

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


inline
bool operator!= (const Time& t1, const Time& t2)
{
  return !(t1 == t2);
}

inline
bool operator> (const Time& t1, const Time& t2)
{
  return t2 < t1;
}

inline
bool operator<= (const Time& t1, const Time& t2)
{
  return !(t1 > t2);
}

inline
bool operator>= (const Time& t1, const Time& t2)
{
  return !(t1 < t2);
}

#endif // TIMES_H

time.cpp
#include "time.h"
#include <iomanip>

using namespace std;

/**
 * Times in this program are represented by three integers: H, M, & S, representing
 * the hours, minutes, and seconds, respecitvely.
 */



  // Create time objects
Time::Time() // midnight
{
  secondsSinceMidnight = 0;
}


Time::Time (int h, int m, int s)
{
  secondsSinceMidnight = s + 60 * m + 3600*h;
}

  // Access to attributes
int Time::getHours() const
{
  return secondsSinceMidnight / 3600;
}

int Time::getMinutes() const
{
  return (secondsSinceMidnight % 3600) / 60;
}

int Time::getSeconds() const
{
  return secondsSinceMidnight % 60;
}

// Calculations with time
void Time::add (Time delta)
{
  secondsSinceMidnight += delta.secondsSinceMidnight;
}
      

Time Time::difference (Time fromTime)
{
  Time diff;
  diff.secondsSinceMidnight = 
    secondsSinceMidnight - fromTime.secondsSinceMidnight;
}
  
  
/**
 * Read a time (in the format HH:MM:SS) after skipping any
 * prior whitepace.
 */
void Time::read (std::istream& in)
{
  char c;
  int hours, minutes, seconds;
  in >> hours >> c >> minutes >> c >> seconds;
  Time t (hours, minutes, seconds);
  secondsSinceMidnight = t.secondsSinceMidnight;
}


/**
 * Print a time in the format HH:MM:SS (two digits each)
 */
void Time::print (std::ostream& out) const
{
  out << setfill('0') << setw(2) << getHours() << ':' 
      << setfill('0') << setw(2) << getMinutes() << ':' 
      << setfill('0') << setw(2) << getSeconds();
}



// Comparison operators
bool Time::operator< (const Time& time2) const
{
  return secondsSinceMidnight < time2.secondsSinceMidnight;
}

bool Time::operator==(const Time& time2) const
{
  return secondsSinceMidnight == time2.secondsSinceMidnight;
}

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
#ifndef TIMES_H
#define TIMES_H

#include <iostream>

/**
 * Times in this program are represented by three integers: H, M, & S, representing
 * the hours, minutes, and seconds, respecitvely.
 */


class Time {
public:
  // Create time objects
  Time(); // midnight
  Time (int h, int m, int s);

  // Access to attributes
  int getHours() const;
  int getMinutes() const;
  int getSeconds() const;

  // Calculations with time
  void add (Time delta);
  Time difference (Time fromTime);
  
  /**
   * Read a time (in the format HH:MM:SS) after skipping any
   * prior whitepace.
   */
  void read (std::istream& in);


  /**
   * Print a time in the format HH:MM:SS (two digits each)
   */
  void print (std::ostream& out) const;



  // Comparison operators
  bool operator== (const Time&) const;
  bool operator< (const Time&) const;

private:
  // From here on is hidden
  int secondsSinceMidnight;
};

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


inline
bool operator!= (const Time& t1, const Time& t2)
{
  return !(t1 == t2);
}

inline
bool operator> (const Time& t1, const Time& t2)
{
  return t2 < t1;
}

inline
bool operator<= (const Time& t1, const Time& t2)
{
  return !(t1 > t2);
}

inline
bool operator>= (const Time& t1, const Time& t2)
{
  return !(t1 < t2);
}

#endif // TIMES_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
#ifndef ARRAYUTILS_H
#define ARRAYUTILS_H



//  Add to the end
//  - Assumes that we have a separate integer (size) indicating how
//     many elements are in the array
//  - and that the "true" size of the array is at least one larger 
//      than the current value of that counter
template <typename T>
void addToEnd (T* array, int& size, T value)
{
   array[size] = value;
   ++size;
}



// Add value into array[index], shifting all elements already in positions
//    index..size-1 up one, to make room.
//  - Assumes that we have a separate integer (size) indicating how
//     many elements are in the array
//  - and that the "true" size of the array is at least one larger 
//      than the current value of that counter

template <typename T>
void addElement (T* array, int& size, int index, T value)
{
  // Make room for the insertion
  int toBeMoved = size - 1;
  while (toBeMoved >= index) {
    array[toBeMoved+1] = array[toBeMoved];
    --toBeMoved;
  }
  // Insert the new value
  array[index] = value;
  ++size;
}


// Assume the elements of the array are already in order
// Find the position where value could be added to keep
//    everything in order, and insert it there.
// Return the position where it was inserted
//  - Assumes that we have a separate integer (size) indicating how
//     many elements are in the array
//  - and that the "true" size of the array is at least one larger 
//      than the current value of that counter

template <typename T>
int addInOrder (T* array, int& size, T value)
{
  // Make room for the insertion
  int toBeMoved = size - 1;
  while (toBeMoved >= 0 && value < array[toBeMoved]) {
    array[toBeMoved+1] = array[toBeMoved];
    --toBeMoved;
  }
  // Insert the new value
  array[toBeMoved+1] = value;
  ++size;
  return toBeMoved+1;
}


// Search an array for a given value, returning the index where 
//    found or -1 if not found.
template <typename T>
int seqSearch(const T list[], int listLength, T searchItem)
{
    int loc;

    for (loc = 0; loc < listLength; loc++)
        if (list[loc] == searchItem)
            return loc;

    return -1;
}


// Search an ordered array for a given value, returning the index where 
//    found or -1 if not found.
template <typename T>
int seqOrderedSearch(const T list[], int listLength, T searchItem)
{
    int loc = 0;

    while (loc < listLength && list[loc] < searchItem)
      {
       ++loc;
      }
    if (loc < listLength && list[loc] == searchItem)
       return loc;
    else
       return -1;
}


// Removes an element from the indicated position in the array, moving
// all elements in higher positions down one to fill in the gap.
template <typename T>
void removeElement (T* array, int& size, int index)
{
  int toBeMoved = index + 1;
  while (toBeMoved < size) {
    array[toBeMoved] = array[toBeMoved+1];
    ++toBeMoved;
  }
  --size;
}



// Search an ordered array for a given value, returning the index where 
//    found or -1 if not found.
template <typename T>
int binarySearch(const T list[], int listLength, T searchItem)
{
    int first = 0;
    int last = listLength - 1;
    int mid;

    bool found = false;

    while (first <= last && !found)
    {
        mid = (first + last) / 2;

        if (list[mid] == searchItem)
            found = true;
        else 
            if (searchItem < list[mid])
                last = mid - 1;
            else
                first = mid + 1;
    }

    if (found) 
        return mid;
    else
        return -1;
}





#endif