Introduction to C++: Lesson 5
Operator Overloading
| Overview: | |
| Operator Overloading |
Operator overloading provides you the ability to have different assignment
operators like +, -, *, <<, !=, [], >, and < for the classes. Normally, you can
use those arithmetic for the normal variables. But for classes, it doesn't
understand those and it won't recognize those symbols + or - or others. It will
understand it if you tell the class what it is. By overloading the operators,
you provides the class the flexibility to choose which operator for the
appropriate action. To understand the operator overloading, let's us first look
at the the most famous String class that you will see in every C++ book
nowadays.
overload.h
#ifndef _OVERLOAD_H_#define _OVERLOAD_H_/************************************************** * * Overload: class maintain overload attributes * * * * **************************************************/ #include <iostream>#include <string>using namespace std;class String { public:String( ); // default constructorString( char *theString); //explicit constructorString( const String& thestring); // copy constructor (note: must have operator=() too)~String(); // nothing to doString& operator=(const String& thestring); // assignment operatorString& operator=(const char *thestring); // overload assignment operatorString operator + (String& myString); // + operatorString operator + (char *myString); //overloaded + operator short operator == (String myString); friend ostream &operator << (ostream &stream, String &showString);private: // the time components char* m_str; int m_len;
}; // end of String class
#endif |
In this header file, we create a String class that has a default constructor, explicit constructor, and the copy constructor. Then, we create a destructor of the string. Next, we create a two assignment operator so that we can assign two classes to each other like class2 = class1 etc... Next, we have the operator + which is the + operator for allowing the compiler to add two classes like class1 + class2 etc...we then overloaded the + and = operators so that if we happen to assign only char then the class will choose the char = to do the assignment, same thing for the +. The next function we have is the operator == which allows us to compare two classes like class1 == class2 etc... Next, we have the ostream << operator that displays the string information. It uses << from this function to display the data but it doesn't use the << from the iostream. We have a new keyword, friend, which allows the function to have the same access rights like other class functions but it doesn't belong to that class. In private, we have two variables, a char pointer and the integer len.
Now, let's us look at the implementation:
overload.cpp
|
/************************************************** * * overload.cpp: Implementation of overload.h * * * **************************************************/ #include "overload.h"String::String( ) { m_str = NULL; m_len = 0; } // default constructorString::String( char* theString) {m_len = strlen(theString); m_str = new char[m_len + 1]; //allocate enough memory for storingstrcpy(m_str,theString); } //explicit constructorString::String( const String& thestring) {m_len = thestring.m_len; m_str = new char[m_len + 1];strcpy(m_str,thestring.m_str); } // copy constructor (note: must have operator=() too)String::~String() { delete m_str;} // nothing to doString& String:: operator=(const String& thestring) { if (this != &thestring) { if (m_str) { delete m_str;} m_len = thestring.m_len; m_str = new char[m_len + 1];strcpy(m_str,thestring.m_str); } return *this;} // assignment operator
String& String:: operator=(const char *thestring) { if (m_str) { delete m_str;} m_len = strlen(thestring); m_str = new char[m_len + 1];strcpy(m_str,thestring); return *this;} // overload assignment operatorString String:: operator + (String& myString) {String tempo; tempo.m_len = m_len + myString.m_len; tempo.m_str = new char[m_len + myString.m_len + 1];strcpy(tempo.m_str,m_str); strcat(tempo.m_str,myString.m_str); return tempo;} // + operatorString String:: operator + (char *myString) {String tempo; int tempLen = strlen(myString);tempo.m_len = m_len + tempLen; tempo.m_str = new char[m_len + tempLen + 1];strcpy(tempo.m_str,m_str); strcat(tempo.m_str,myString); return tempo;} //overloaded + operatorshort String::operator == (String myString) { return((strcmp(m_str,myString.m_str) == 0) ? 1 : 0);} //comparing two stringsostream & operator << (ostream &stream, String &showString) {stream << showString.m_str; return stream;}
|
Implementation: First, we're setting the default constructor to NULL value because it doesn't need to have any characters.
Then we have the explicit and copy constructor. In both of these constructors, we have something similar:
m_len = strlen(theString); ===> this line is to get the string length and assign it to the m_len
m_str =
new char[m_len + 1]; //allocate enough memory for storingWe then allocate enough memory for the new word create by adding extra one at the end.
strcpy(m_str,theString); we then copy the string into the m_str. Notice, this is the most typical structure that you will use throughout the string class or when you want to create one.
We then have two assignment operators functions. One thing you need to keep notice that for the assignment operator you will need to do two things: first is to check and delete if there is any existence dynamic members, and then you can allocate new space for doing copy if you need to. The typical structure for the assignment operator is as follow:
if (this != &thestring) {//do something here for the assignment operator
}
return *this;Now, you see the *this again. Remember that the this pointer is a class pointer to the object calling the member function.
The + and the == operators functions are straight forward.
Now, let's us look at the main file for this:
main.cpp
|
/************************************************** * * Overloading Class Implementation * * Change history: * **************************************************/ #pragma warning(disable:4786)#include <iostream>#include "overload.h"int main(){ String s1 = "This is String One"; String s2 = "This is String Two"; cout << s1 << endl; cout << s2 << endl; String s3; s3 = s1 + " " + s2; cout << "s1 + s2 = s3: " << s3 << endl; if (s1 == s3) {cout << "s1 = s3" << endl; } else {cout << "s1 not equal s3" << endl; } return 0; }
|
In the main file, we created two string with preset text then we added the two strings and assigned it to the string 3. The if statement checks to see if s1 is equal s3, which is not and it will not.
Here is our output:

Next session, we will cover the inheritance, multiple inheritance using STL, and of course indepth STL. Here is a two quick simple classes that covers the topics we went through:
The Player Class:
Player.h
| #ifndef
_PLAYER_H_ #define _PLAYER_H_/************************************************** * * PLAYER: class maintain player attributes * * * * **************************************************/ #include <iostream>#include <string>using namespace std;class Player { public:Player( ); // default constructorPlayer( int lives, int health, double points); // explicit constructorPlayer( const Player& player); // copy constructor (note: must have operator=() too) virtual ~Player() { };// nothing to do //set stuff for players void setLives(int thelives); void setHealth(int thehealth); void SetName(const char* name); void setPoints(double points); //get stuff for players const int getLives() const; const int getHealth() const; const char* GetName() const; const double getPoints() const; //show information of player virtual void show() const;Player& operator=(const Player& player); // assignment operator protected: // the time components int m_lives; int m_health; double m_points; char m_name[128]; mutable int m_namecalled;}; // end of Player class
#endif
|
The Implementation: player.cpp
|
/************************************************** * * Player.cpp: Implementation of player.h * * * **************************************************/ #include "Player.h"
// default constructor Player::Player() : m_lives(3), m_health(100), m_points(0) { }; // explicit constructor Player::Player( int lives, int health, double points) :m_lives(lives), m_points(points), m_health(health) { }
// copy constructor Player::Player( const Player& player) :m_lives(player.m_lives), m_health(player.m_health), m_points(player.m_points) { int n = strlen(player.m_name); int i(0); for (i = 0; i < n && player.m_name[i]; i++)m_name[i] = player.m_name[i]; while (i <= n)m_name[i++] = '\0';
}
//set stuff for players void Player::setLives(int thelives) {m_lives = thelives; }
void Player::setHealth(int thehealth) {m_health = thehealth; }
void Player::SetName(const char* name) {::strcpy(m_name, name); }
void Player::setPoints(double points) {m_points = points; }
//get stuff for players const int Player::getLives() const { return m_lives;}
const int Player::getHealth() const { return m_health;}
const char* Player::GetName() const {++m_namecalled; return m_name;}
const double Player::getPoints() const { return m_points;}
void Player::show() const {cout << m_name << endl; cout << m_lives << endl; cout << m_health << endl; cout << m_points << endl; } // assignment operator Player& Player:: operator=(const Player& player) { if (this != &player) { // test for self-assignment (player1 = player2;)m_lives = player.m_lives; m_health = player.m_health; m_points = player.m_points; int n = strlen(player.m_name); int i(0); for (i = 0; i < n && player.m_name[i]; i++)m_name[i] = player.m_name[i]; while (i <= n)m_name[i++] = '\0'; } return *this;}
|
Main: main.cpp
|
/************************************************** * * Player Class Implementation * * Change history: * **************************************************/ #pragma warning(disable:4786)#include <iostream>#include "Player.h"int main(){ //testing functions for Player classPlayer playerOne; playerOne.SetName("Smith"); playerOne.setLives(3); playerOne.setHealth(100); playerOne.setPoints(0); cout << "Player One Information: regular assignment through functions call" << endl; playerOne.show(); cout << endl; //testing for assignment operatorPlayer playertwo; playertwo = playerOne; cout << "Player Two Information: using assignment operator" << endl; playertwo.show(); cout << endl; //testing for copy constructorPlayer playerthree(playertwo); cout << "Player Three Information: using copy constructor" << endl; playerthree.show(); cout << endl; return 0;}
|
The player class is basically setting the name, lives, health, and points, then display it to the screen by using the three different constructors.
mutable int m_namecalled;
The keyword, mutable, allows to increment the const variable.
Here is our screenshot:

Here is another class to look at: Time()
Time.h
| #ifndef
_TIME_H_ #define _TIME_H_/************************************************** * * Time: class the maintain a time of day * **************************************************/ #include <string>using namespace std;class Time { public:Time( ); // default constructor, initializes time to 12:00 MidnightTime( long hr, long min, long sec, char ampm); // explicit constructorTime( const Time& time); // copy constructor (note: must have operator=() too)~Time() { }; // nothing to do void setHours(long h); void setMinutes(long m); void setSeconds(long s); void setAmpm(char ampm); long getHours() const; long getMinutes() const; long getSeconds() const; char getAmpm() const;Time& operator=(const Time& time); // assignment operatorstring timeOfDay(); // return the time of dayvoid update(); //update the time automatically private: // the time components long _hours; long _minutes; long _seconds; char _ampm; // 'A' for AM, 'P' for PM //private member function of intervalTimer() // This function returns the clock time in seconds since the // previous call. The first call returns 0.0. double intervalTimer() { static bool firstCall = true; // true if this is the first time calling this function static clock_t fromTime; // save previous clock time double duration(0.0);clock_t toTime = clock(); // get the current clock time if (firstCall) { // first callfirstCall = false;} else { // not the first callduration = ( double)(toTime - fromTime)/CLOCKS_PER_SEC;} fromTime = toTime; // save the current time for next call return long(duration);} }; // end of Time class
// inline member functions // default constructor inline Time::Time() :_hours(12), _minutes(0), _seconds(0), _ampm('A') { intervalTimer(); }; #endif |
time.cpp
|
/************************************************** * * implementation of Time class * 421 * Local include: Time.h * **************************************************/ #include <iomanip>#include <iostream>#include <sstream>#include <cassert>#include <ctime>#include "Time.h"
// explicit constructor Time::Time( long hr, long min, long sec, char ampm) :_hours(hr), _minutes(min), _seconds(sec), _ampm(ampm) { // validationsassert((hr < 23) && (hr > 0)); assert((min < 60) && (min >= 0)); //assert((sec < 60) && (sec >= 0));intervalTimer(); }
// copy constructor Time::Time( const Time& time) :_hours(time._hours), _minutes(time._minutes), _seconds(time._seconds), _ampm(time._ampm) { intervalTimer(); } // accessor members void Time::setHours(long h){ if ((h>0) && (h < 23)) {_hours = h; } } void Time::setMinutes(long m){ if ((m>=0) && (m < 60)) {_minutes = m; } } void Time::setSeconds(long s){ if ((s>=0) && (s < 60)) {_seconds = s; } } void Time::setAmpm(char ampm){ switch (ampm) { case 'A':_ampm = 'A'; break; case 'B':_ampm = 'B'; break;} } long Time::getHours() const{ return _hours;} long Time::getMinutes() const{ return _minutes;} long Time::getSeconds() const{ return _seconds;} char Time::getAmpm() const{ return _ampm;} Time& Time:: operator=(const Time& time) // assignment operator{ if (this != &time) { // test for self-assignment (time1 = time2;)_hours = time._hours; _minutes = time._minutes; _seconds = time._seconds; _ampm = time._ampm; } return *this;} string Time::timeOfDay() // return the time of day in a string{ ostringstream oss; oss << _hours << ':' << setfill('0') << setw(2) << _minutes << '.' << setw(2) << _seconds << ' ' << (_ampm == 'A'? "AM" : "PM"); return oss.str();}
void Time::update(){ long elapsedTime; //call the intervalTimer function and assign the time to elapsedTimeelapsedTime = intervalTimer(); cout << "Elapsed Time: " << elapsedTime << endl;
if (_seconds > 59) { //adjust the minutes and hours _minutes++; _seconds = elapsedTime; } if (_minutes > 59) { //adjust the minutes and hours_minutes = ++elapsedTime; ++_hours; } if (_hours > 24) { //adjust the minutes and hours_minutes = elapsedTime; _hours = elapsedTime; _seconds = elapsedTime; }
}
|
Time: main.cpp
|
/************************************************** * * main.cpp: test driver for Time class * * Requires: Test.h, fstream, sstream * * **************************************************/ #include <string>#include <iostream>#include <iomanip>#include <time.h>#include <cstdlib>#include "Time.h"using namespace std;
int main (int argc, char* argv[]){ // testing the basic constructor and accessorsTime tod1; cout << "The time is now " << tod1.getHours() << ':' << setw(2) << setfill('0') << tod1.getMinutes() << '.' << setw(2) << tod1.getSeconds() << ' ' << tod1.getAmpm() << endl; //basic constructor and accessors above is working fine// testing the copy constructor and more accessors Time tod2(10,0,60,'P'); //assertion failed if seconds is at 60tod2.setAmpm('A'); /* fail */ tod2.setHours(20); //sethours is not working because the sethours here exceeds the range defined in the time classtod2.setMinutes(2); //testing setminutes.....it is working finetod2.setSeconds(60); //testing the setseconds method, setseconds is not working when setting seconds at 60tod2.update(); //call update functioncout << "The time 2 is now " << tod2.getHours() << ':' << setw(2) << tod2.getMinutes() << '.' << setw(2) << tod2.getSeconds() << ' ' << tod2.getAmpm() << endl; // testing the assignment operator (operator=())Time tod3 = tod2; //assignment operator is working finestring timeOfDay = tod3.timeOfDay(); cout << timeOfDay << endl;
cout << "Ending test \n"; return 0;}
|
The time class displays the time and updates it accordingly based on user defined. Feel free to look at the Player and Time classes again for how the class was implemented. Most of the functions are pretty much a review.
Assignments:
1. 3D Polar Class
- Create a 3D Polar class that represents the points on the plain as polar coordinates (radius and angle). Create an overloaded +operator for addition of the Polar quantities. "Adding" two points on the plain can be accomplished by adding their X, Y, and Z coordinates. Convert the sets of polar coordinates to rectangular coordinates, add them, then convert the resulting rectangular representation back to polar.
2. Time Class
- use the time class from this lesson and modify it by adding overloaded +, - and * operators to the class. This will give the class the ability to add time, minus time or multiply time.
3. overload.h class or string class
- use the overload.h (string class) from the beginning of this lesson and add in the - operator to the class. This will give the string class the ability to add string.