You are on page 1of 40

Introduction

This handout is intended to help the students read the textbook C++ Programming: From Problem
Analysis to Program Design. Extra examples were added to this handout and hints also included. This
handout is not a substitute of the textbook and should be used in parallel with it.

In this handout all type declaration where typed in boldface and all struct variables and class objects
were typed in italic face.

Summary of Chapter 15, page 801 from the book


C++ Programming: From Problem Analysis to Program Design
By: D.S. Malik
2nd edition, 2004
Thomson Course Technology
ISBN: 0-619-16042-X

1
Overloading

In C++, several functions can have the same name; this concept is called overloading a function name.
In overloading every function must have:
 A different number of formal parameters, or
 If the number of formal parameters is the same, then the data type of the formal parameters, in
the order you list, must differ in at least one position.

The types of parameters determine which function to execute.

Example: Suppose that we want to determine the larger of two items. Both items can be integers,
characters, floating-point numbers, or strings. The functions prototypes are

int largerInt(int x, int y);


char largerChar(char first, char second);
double largerDouble(double u, double v);
string largerString(string a, string b);

All of the above functions perform similar operations, and so instead of giving different names to
these functions, we can use the same name, say larger; that is we can overload the function larger.

int larger(int x, int y);


char larger(char first, char second);
double larger(double u, double v);
string larger(string a, string b);

If the call is larger(5, 3), then the first function executes, and if the call is larger('A', '9') then the
second function executes.

Although the same function name is used, however different function definitions must be included in
the program.

Why Operator Overloading is Needed: Consider the following statements


clockType myClock(8, 23, 34);
clockType yourClock(4, 5, 30);

Now consider the following statements:


myClock.printTime( );
myClock.incrementSeconds( );
if (myClock.equalTime(yourClock))
.
.
.
These statements do their job. However, if we can use the insertion operator « to output the value of
myClock, the increment operator ++ to increment the value of myClock by one second, and relational
operators for comparison, we can enhance the flexibility of C++ considerably. More specifically, we
prefer to use the following statements instead of the previous statements:

2
cout << myClock;
myClock++;
if (myClock == yourClock)

Recall that the only built-in operations on classes are the assignment operator and the member
selection operator. Therefore, other operators cannot be directly applied to class objects. However, C+
+ allows the programmer to extend the definitions of most of the operators so that operators--such as
relational operators, arithmetic operators, the insertion operator for data output, and the extraction
operator for data input, can be applied to classes. In C++ terminology, this is called operator
overloading.

Operator Overloading
Recall how the arithmetic operator / works. If both operands of / are integers, the result is an integer;
otherwise, the result is a floating-point number. Similarly, the stream insertion operator, <<, and the
stream extraction operator, >>, are overloaded. The operator >> is used as both a stream extraction
operator and a right shift operator. The operator << is used as both a stream insertion operator and a
left shift operator. These are examples of operator overloading.
Other examples of overloaded operators are + and -. The results of + and - are different for integer
arithmetic, floating-point arithmetic, and pointer arithmetic. C++ allows the user to overload most of
the operators so that the operators can work effectively in a specific application. It does not allow the
user to create new operators. Most of the existing operators can be overloaded to manipulate class
objects.
In order to overload operators, you must write functions (that is, the header and body). The name of
the function that overloads an operator is the reserved word operator followed by the operator to be
overloaded. For example, the name of the function to overload the operator >= is: operator >=( )
Operator function: The function that overloads an operator.

Syntax for Operator Functions


The result of an operation is a value. Therefore, the operator function is a value-returning function.
The syntax of the heading for an operator function is:
returnType operator operatorSymbol(formal parameter list)
In C++, operator is a reserved word.

To overload an operator for a class:


 Include the statement to declare the function to overload the operator (that is, the operator
function) prototype in the definition of the class.
 Write the definition of the operator function.

Overloading an Operator: Some Restrictions


When overloading an operator, keep the following in mind:
1. You cannot change the precedence of an operator.

3
2. The associativity cannot be changed. (For example, the associativity of the arithmetic operator
addition is from left to right, and it cannot be changed.)
3. Default arguments cannot be used with an overloaded operator.
4. You cannot change the number of arguments an operator takes.
5. You cannot create new operators. Only existing operators can be overloaded.
6. The operators that cannot be overloaded are:
. .* :: ?: sizeof
7. The meaning of how an operator works with built-in types, such as int, remains the same.
8. Operators can be overloaded either for objects of the user-defined types, or for a combination
of objects of the user-defined type and objects of the built-in type.

Friend Functions of Classes


A function that is defined outside the scope of a class is called a friend function of the class. A friend
function is a nonmember function of the class, but has access to the private data members of the
class. To make a function be a fiend to a class, the reserved word friend precedes the function
prototype (in the class definition). The word friend appears only in the function prototype in the class
definition, not in the definition of the fiend function.
Consider the following statements:
class classIllusFriend
{

friend void two(...);

};

In the definition of the class classIllusFriend, two is declared as a friend of the class
classIllusFriend. That is, it is a nonmember function of the class classIllusFriend. When you write
the definition of the function two( ), any object of the type classIllusFriend - which is either a local
variable of two( ) or a formal parameter of two( ) - can access its private members within the
definition of the function two( ). Moreover, because a friend function is not a member of a class, its
declaration can be placed within the private, protected, or public part of the class. However, they are
typically placed before any member function declaration.

Definition of a friend Function


When writing the definition of a friend function, the name of the class and the scope resolution
operator do not precede the name of the friend function in the function heading. Also, recall that the
word friend does not appear in the heading of the friend function's definition. Thus, the definition of
the function two in the previous class classIllusFriend is:
void two(// parameters)
{

}
Of course, we will place the definition of the friend function in the implementation file.

4
The following example shows how a friend function accesses the private member of a class.

Example: Consider the following class:

class classIllusFriend
{
friend void two(classIllusFriend cIFObject);
public:
void print( );
void setX(int a);
private:
int x;
};

In the definition of the class classIllusFriend, two is declared as a friend function. Suppose that the
definitions of the member functions of the class classIllusFriend are as follows:

void classIllusFriend::print( )
{
cout << "In class classIllusFriend: x = " << x << endl;
}

void classIllusFriend::setX(int a)
{
x = a;
}

Consider the following definition of the function two( ):


void two(classIllusFriend cIFObject)
{
classIllusFriend localTwoObject;

localTwoObject.x = 45;

localTwoObject.print( );
cout << endl;
cout << "In Friend Function two accessing private data member x = "
<< localTwoObject.x << endl;

cIFObject.x = 88;

cIFObject.print( );

cout << endl;


cout << "In Friend Function two private data member x = " << cIFObject.x << endl;
}

The function two contains a formal parameter cIFObject and a local variable localTwoObject, both of

5
the type classIllusFriend. The object localTwoObject accesses its private data member x and sets its
value to 45. If the function two( ) is not declared as a friend function of the class classIllusFriend,
then this statement would result in a syntax error because an object cannot directly access its private
members. Similarly, the formal parameter cIFObject accesses its private data member x and sets its
value to 88. Once again, this statement would result in a syntax error if two( ) is not declared a friend
function of the class classIllusFriend.

Now consider the definition of the following function main:


int main( )
{
classIllusFriend aObject;

aObject.setX(32);

cout << "aObject.x: ";


aObject.print( );
cout << endl;
cout << "*-*-*-*-*-* Testing Friend Function two *-*-*-*-*-*" << endl << endl;

two(aObject);

return 0;
}

Sample Run

aObject.x: In class classIllusFriend: x = 32

*-*-*-*-*-* Testing Friend Function two *-*-*-*-*-*

In class classIllusFriend: x = 45

In Friend Function two accessing private data member x = 45


In class classIllusFriend: x = 88

In Friend Function two accessing private data member x = 88

Operator Functions as Member Functions and Nonmember Functions


Certain rules must be followed when you include an operator function in the definition of a class. The
following is a description of these rules.
Most operator functions can be either member functions or nonmember functions-that is, friend
functions of a class. To make an operator function be a member or nonmember function of a class,
keep the following in mind:
1. The function that overloads any of the operators ( ), [ ], ->, or = for a class must be declared as
a member of the class.
2. Suppose that an operator op is overloaded for a class---say, OpOverClass. (Here, op stands

6
for an operator that can be overloaded, such as + or >>)
a. If the leftmost operand of op is an object of a different type (that is, not of the type
OpOverClass), the function that overloads the operator op for OpOverClass must be a
nonmember-that is, a friend of the class OpOverClass.
b. If the operator function that overloads the operator op for the class OpOverClass is a
member of the class OpOverClass, then when applying op on objects of the type
OpOverClass, the leftmost operand of op must be of the type OpOverClass.

You must follow these rules when including an operator function in a class definition.
You will see later in this chapter that functions that overload the insertion operator, <<, and the
extraction operator, >>, for a class must be nonmembers-that is, friend functions of the class.

Except for certain operators noted previously, operators can be overloaded either as member functions
or as nonmember functions. The following discussion shows the difference between these two types of
functions.
To facilitate our discussion of operator overloading, we will use the class rectangle. The class was
discussed before and hence we will write the class interface (definition) only.

class rectangle
{
public:
void set(double l, double w);
double getLength( ) const;
double getWidth( ) const;
double area( ) const;
double perimeter( ) const;
void print( ) const;
rectangle( );
rectangle(double l, double w);
private:
double length, width;
};

Suppose that you have the following statements:

rectangle myRectangle;
rectangle yourRectangle;
rectangle tempRect;
That is, myRectangle, yourRectangle, and tempRect are objects of the type rectangle.
C++ consists of both binary and unary operators. It also has a ternary operator, which cannot be
overloaded.

Overloading Binary Operators


Suppose that # represents a binary operator (arithmetic, such as +; or relational, such as ==) that is to
be overloaded for the class rectangle. This operator can be overloaded as either a member function of
the class or as a friend function. We will describe both ways to overload this operator.

7
Overloading the Binary Operators as Member Functions
Suppose that # is overloaded as a member function of the class rectangle. The name of the function to
overload # for the class rectangle is: operator#( )
Because myRectangle and yourRectangle are objects of the type rectangle, you can perform the
operation:

myRectangle # yourRectangle

The compiler translates this expression into the following expression:


myRectangle.operator# (yourRectangle)
This expression clearly shows that the function operator# has only one argument, which is
yourRectangle.

Because operator# is a member of the class rectangle and myRectangle is an object of the type
rectangle, in the previous statement, operator# has direct access to the private members of the object
myRectangle. Thus, the first argument of operator# is the object that is invoking the function
operator#, and the second argument is passed as a parameter to this function.

General Syntax to Overload the Binary (Arithmetic or Relational) Operators as Member


Functions
This section describes the general form of the functions to overload the binary operators as member
functions of a class.

Function Prototype (to be included in the definition of the class):


returnType operator# (const className&) const;
where # stands for the binary operator, arithmetic or relational, to be overloaded; returnType is the
type of value returned by the function; and className is the name of the class for which the operator
is being overloaded.

Example: Let us overload +, -, *, ==, and != as member functions for the class rectangle.

// rectangle.h

#ifndef H_rectangle
#define H_rectangle

class rectangle
{
public:
void set(double l, double w);
double getLength( ) const;
double getWidth( ) const;
double area( ) const;
double perimeter( ) const;
void print( ) const;
rectangle operator+(const rectangle &) const;

8
rectangle operator-(const rectangle &) const;
rectangle operator*(const rectangle &) const;
bool operator==(const rectangle &) const;
bool operator!=(const rectangle &) const;
rectangle ( );
rectangle (double l, double w);
private:
double length, width;
};
#endif

The implementation file is

// rectangleImp.cpp

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

using namespace std;

void rectangle::set(double l, double w)


{
if ( l >= 0 )
length = l;
else
length = 0;

if ( w >= 0 )
width = w;
else
width = 0;
}

double rectangle::getLength( ) const


{
return length;
}

double rectangle::getWidth( ) const


{
return width;
}

double rectangle::area( ) const


{
return length * width;
}

9
void rectangle::print( ) const
{
cout << "Length = " << length << " Width = " << width;
}

rectangle::rectangle( )
{
length = width = 0;
}

rectangle:: rectangle( double l, double w )


{
set( l, w );
}

rectangle rectangle::operator+(const rectangle& rectObj) const


{
rectangle tempRect;

tempRect.length = length + rectObj.length;


tempRect.width = width + rectObj.width;

return tempRect;
}

rectangle rectangle::operator-(const rectangle& rectObj) const


{
rectangle tempRect;

tempRect.length = length - rectObj.length;


tempRect.width = width - rectObj.width;

return tempRect;
}

rectangle rectangle::operator*(const rectangle& rectObj) const


{
rectangle tempRect;

tempRect.length = length * rectObj.length;


tempRect.width = width * rectObj.width;

return tempRect;
}

bool rectangle::operator==(const rectangle& rectObj) const


{
return (length == rectObj.length &&
width == rectObj.width);
}

10
bool rectangle::operator!=(const rectangle& rectObj) const
{
return (length != rectObj.length || width != rectObj.width);
}

Notice that operator# acts on the left operand and uses the right operand as the function actual
parameter (i.e.: length is the length of the left operand, while rectObj.length is the length of the right
operand).

Consider the following program. (We assume that the definition of the class rectangle is in the header
file rectangle.h)

// rectangle.cpp

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

using namespace std;

int main( )
{
rectangle rectangle1(23, 45);
rectangle rectangle2(12, 10);
rectangle rectangle3;
rectangle rectangle4;
rectangle rectangle5;
rectangle rectangle6;

cout << "rectangle1: ";


rectangle1.print( );
cout << endl;

cout << "rectangle2: ";


rectangle2.print( );
cout << endl;

rectangle3 = rectangle1 + rectangle2;

cout << "rectangle3: ";


rectangle3.print( );
cout << endl;

rectangle4 = rectangle1 - rectangle2;

cout << "rectangle4: ";


rectangle4.print( );
cout << endl;

rectangle5 = rectangle2 - rectangle1;

11
cout << "rectangle5: ";
rectangle5.print( );
cout << endl;

rectangle6 = rectangle1 * rectangle2;

cout << "rectangle6: ";


rectangle6.print( );
cout << endl;

if (rectangle1 == rectangle2)
cout << "rectangle1 and rectangle2 are equal." << endl;
else
cout << "rectangle1 and rectangle2 are not equal." << endl;

if (rectangle1 != rectangle3)
cout << "rectangle1 and rectangle3 are not equal." << endl;
else
cout << "rectangle1 and rectangle3 are equal." << endl;

return 0;
}

Sample Run
rectangle1: Length = 23 Width = 45
rectangle2: Length = 12 Width = 10
rectangle3: Length = 35 Width = 55
rectangle4: Length = 11 Width = 35
rectangle5: Length = -11 Width = -35
rectangle6: Length = 276 Width = 450
rectangle1 and rectangle2 are not equal.
rectangle1 and rectangle3 are not equal.

Overloading the Binary Operators (Arithmetic or Relational) as Nonmember Functions


Suppose that # represents the binary operator (arithmetic or relational) that is to be overloaded as a
nonmember function of the class rectangle. Further suppose that the following operation is to be
performed:
myRectangle # yourRectangle
In this case, the expression is compiled as:
operator#(myRectangle, yourRectangle)
Here, we see that the function operator# has two arguments. This expression also clearly shows that
the function operator#( ) is neither a member of the object myRectangle nor a member of the object
yourRectangle. The objects to be added are passed as arguments to the function operator#( ).
To include the operator function operator#( ) as a nonmember function of the class in the definition of
the class, the reserved word friend must appear before the function heading. Also, the function
operator#( ) must have two arguments.

12
General Syntax to Overload the Binary (Arithmetic or Relational) Operators as Nonmember
Functions
This section describes the general form of the functions to overload the binary operators as
nonmember functions of a class.

Function Prototype (to be included in the definition of the class):


friend returnType operator# (const className&, const className&);
where # stands for the binary operator to be overloaded, returnType is the type of value returned by
the function, and className is the name of the class for which the operator is being overloaded.

returnType operator# (const className& firstObject, const className& secondObject)


{
//algorithm to perform the operation

return value;
}

Example: This example illustrates how to overload the operators + and == as nonmember functions of
the class rectangle.
To include the operator function operator+( ) as a nonmember function of the class rectangle, its
prototype in the definition of rectangle is:

friend rectangle operator+ (const rectangle&, const rectangle&);

The definition of the function operator+( ) is as follows:

rectangle operator+ (const rectangle& firstRect, const rectangle& secondRect)


{
rectangle tempRect;

tempRect.length = firstRect.length + secondRect.length;


tempRect.width = firstRect.width + secondRect.width;

return tempRect;
}

To include the operator function operator==( ) as a nonmember function of the class rectangle, its
prototype in the definition of rectangle is:

friend bool operator== (const rectangle&, const rectangle&);

The definition of the function operator==( ) is as follows:

bool operator== (const rectangle& firstRect, const rectangle& secondRect)


{
return (firstRect.length == secondRect.length && firstRect.width == secondRect.width);
}

13
Overloading the Stream Insertion (<<) and Extraction (>>) Operators
The operator function that overloads the insertion operator, <<, or the extraction operator, >>, for a
class must be a nonmember function of that class for the following reason. Consider the expression:
cin >> myRectangle
In this expression, the leftmost operand of >> (that is, cin) is an object of the type istream, not an
object of the type rectangle. Because the leftmost operand of >> is not an object of the type
rectangle, the operator function that overloads the extraction operator for rectangle must be a
nonmember function of the class rectangle. Similarly, the operator function that overloads the stream
insertion operator for rectangle must be a nonmember function of rectangle.

Overloading the Stream Insertion Operator (<<)


The general syntax to overload the stream insertion operator, <<, for a class is described next.

Function Prototype (to be included in the definition of the class):


friend ostream& operator<<(ostream&, const className&);
To enable the cascaded insertion of objects, the return type of the function operator<<( ) must be a
reference to an object of the ostream type. The second parameter of the function is const so it can not
be changed.

Function Definition:
ostream& operator<<(ostream& osObject, const className& cObject)
{
// Local declaration, if any
// Output the members of cObject.
// osObject << . . .

// Return the stream object.


return osObject;
}

In this function definition:


 Both parameters are reference parameters.
 The first parameter--that is, osObject-- is a reference to an ostream object.
 The second parameter is usually a const reference to a particular class, because the most
effective way to pass an object as a parameter to a class is by reference. In this case, the formal
parameter does not need to copy the data members of the actual parameter. The word const
appears before the class name because we want to print only the data members of the object.
That is, the function should not modify the data members of the object.
 The function return type is a reference to an ostream object.
The return type of the function to overload the operator << must be a reference to an ostream object
for the following reasons.
Suppose that the operator << is overloaded for the class rectangle. The statement:
cout << myRectangle;

14
is equivalent to the statement:
operator<<(cout, myRectangle);
This is a perfectly legal statement because both of the actual parameters are objects, not the value of
the objects. The first argument, cout, is of the type ostream; the second argument, myRectangle, is of
the type rectangleType.
Now consider the following statement:
cout << myRectangle << yourRectangle;
This statement is equivalent to the statement:
operator<< (operator<< (cout, myRectangle), yourRectangle);
because the associativity of the operator << is from left to right.

To execute the previous statement, you must first execute the expression:
cout << myRectangle
that is, the expression:
operator << (cout, myRectangle)
After executing this expression, which outputs the values of the data members of the object
myRectangle, whatever is returned by the function operator<<( ) will become the left-side argument
of the operator<<( ) - that is, the first parameter of the function operator<<( ) - in order to output the
value of object yourRectangle. Because the left-side argument of the operator<<( ) must be an object
of the ostream type, the expression:
cout << myRectangle
must return the object cout (not its value) in order to output the value of yourRectangle. Therefore, the
return type of the function operator<<( ) must be a reference to an object of the ostream type.

Overloading the Stream Extraction Operator (>>)


The general syntax to overload the stream extraction operator, >>, for a class is described next.

Function Prototype (to be included in the definition of the class):


friend istream& operator>>(istream&, className&);
To enable the cascaded extraction of objects, the return type of the function operator<<( ) must be a
reference to an object of the ostream type. The second parameter of the function is not a const to
allow a value to be saved in the object.

Function Definition:

istream& operator>>(istream& isObject, className& cObject)


{
// Local declaration, if any
// Read the data into cObject.
// isObject >> . . .

15
// Return the stream object.
return isObject;
}

In this function definition:


 Both parameters are reference parameters.
 The first argument-that is, isObject-is a reference to an istream object.
 The second argument is usually a reference to a particular class. The data read will be stored in
the object.
 The function return type is a reference to an istream object.

For the same reasons as explained previously (when we overloaded the insertion operator <<), the
return type of the function operator<<( ) must be a reference to an istream object. We can then
successfully execute statements of the following type:
cin >> myRectangle >> yourRectangle;

The functions implementation of the two operators are

ostream& operator<<(ostream& osObject, const sales& cObject)


{
osObject << …… << ………<< ……….. ;

return osObject;
}

istream& operator>>(istream& isObject, sales& cObject)


{
isObject >> ……… >> ………… >> ………… ;
return isObject;
}

Example: Consider the following example.

// sales.h

#include <iostream>
#include <string>
using namespace std;

#ifndef H_sales
#define H_sales

class sales
{
friend ostream& operator<<(ostream&, const sales&);
friend istream& operator>>(istream&, sales&);

16
public:
void set(string, int, int, int);
void get(string&, int&, int&, int&) const;
void print( ) const;
sales operator+(const sales&) const;
bool operator!=(const sales&) const;
sales(string = "", int = 0, int = 0, int = 0);
private:
string ID;
int radio, television, computer;
};
#endif

// salesImp.cpp

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

using namespace std;

void sales::set(string IDSalesPerson, int radioItem, int televisionItem, int computerItem)


{
ID = IDSalesPerson;
radio = radioItem;
television = televisionItem;
computer = computerItem;
}

void sales::get(string& IDSalesPerson, int& radioItem, int& televisionItem, int& computerItem) const
{
IDSalesPerson = ID;
radioItem = radio;
televisionItem = television;
computerItem = computer;
}

void sales::print( ) const


{
cout << "ID is: " << ID << endl;
cout << "Radio: " << radio << ", TV: " << television << ", Computer: " << computer;
cout << endl << endl;
}

sales::sales(string IDSalesPerson, int radioItem, int televisionItem, int computerItem)


{
set(IDSalesPerson, radioItem, televisionItem, computerItem);
}

17
sales sales::operator+(const sales& salesObject) const
{
sales tempSales;

tempSales.ID = "Total Sales";


tempSales.radio = radio + salesObject.radio;
tempSales.television = television + salesObject.television;
tempSales.computer = computer + salesObject.computer;

return tempSales;
}

bool sales::operator!=(const sales& salesObject) const


{
bool trueFalse;
trueFalse = radio != salesObject.radio || television != salesObject.television;
trueFalse = trueFalse || computer != salesObject.computer;
return trueFalse;
}

ostream& operator<<(ostream& osObject, const sales& cObject)


{
osObject << "ID is: " << cObject.ID << ", Radio: " << cObject.radio
<< ", TV: " << cObject.television << ", Computer: " << cObject.computer;

return osObject;
}

istream& operator>>(istream& isObject, sales& cObject)


{
isObject >> cObject.ID >> cObject.radio >> cObject.television >> cObject.computer;

return isObject;
}

// sales.cpp

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

using namespace std;

int main( )
{
string sID;
int r, t, c, salesPrice;

// the following are the radio unit price, television unit price,

18
// and the computer unit price, respectively.

int radioPrice = 50, televisionPrice = 1200, computerPrice = 800;

sales zero("0000", 0, 0, 0);


sales one, two, three, four;

cout << "Enter values for the three objects, an ID and three integers for each object" << endl;
cin >> one >> two >> three;
cout << one << two << three;

four = one + two + three;

cout << endl;


cout << four;
cout << endl;

if(four != zero)
{
four.get(sID, r, t, c);
salesPrice = radioPrice*r + televisionPrice*t + computerPrice*c;
cout << "Total sales price is: " << salesPrice << endl << endl;
}
else
cout << "No items sold!" << endl << endl;

return 0;
}

Due to a bug in the Visual Studio the friend functions cannot access the private data members of the
class, so the following modifications were used in the above two friend functions (i.e.: the data
members were accessed through the set and get functions of the class)

ostream& operator<<(ostream& osObject, const sales& cObject)


{
string sID;
int r, t, c;
cObject.get(sID, r, t, c);
osObject << "ID is: " << sID << ", Radio: " << r << ", TV: "
<< t << ", Computer: " << c <<endl;

return osObject;
}

istream& operator>>(istream& isObject, sales& cObject)


{
string sID;
int r, t, c;
isObject >> sID >> r >> t >> c;

19
cObject.set(sID, r, t, c);

return isObject;
}

Example:

// array.h

#include <string>

using namespace std;

class Array
{
public:
void set(int);
Array(int = 3);
~Array( );
int & operator[ ](int);
private:
int length, *p;
};

// arrayImp.cpp

#include <iostream>
#include <cassert>
#include "array.h"

using namespace std;

void Array::set(int vLength)


{
length = vLength;

p = new int [length];


assert(p != NULL);

for(int i=0; i<length; i++)


cin >> p[i];
}

Array::Array(int vLength)
{
length = vLength;

20
p = new int [length];

for(int i=0; i<length; i++)


p[i] = 0;
}

Array::~Array( )
{
delete [ ] p;
p = NULL;
}

int & Array::operator[ ](int index)


{
if(index<0 || index >= length)
{
cout << "index is out of range" << endl;
exit(1);
}

return p[index];
}

// array.cpp

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

using namespace std;

int main( )
{
Array u(5);

u.set(5);

cout << u[3] << endl;

return 0;
}

Example:

/* header program */

#include <iostream>

21
using namespace std;

class Matrix
{
public:
Matrix(unsigned, unsigned);
double& operator( )(unsigned, unsigned);
~Matrix( );

private:
unsigned numberOfRows, numberOfColumns;
double *data;
};

/* implementation program */

#include <iostream>
#include <cassert>
#include "header.h"

using namespace std;

Matrix::Matrix(unsigned rows, unsigned cols)


{
numberOfRows = rows;
numberOfColumns = cols;
data = new double[numberOfRows * numberOfColumns];
assert(data != NULL);
}

Matrix::~Matrix( )
{
delete [ ] data;
data = NULL;
}

double& Matrix::operator( )(unsigned row, unsigned col)


{
return data[numberOfColumns*row + col];
}

/* driver program */

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

using namespace std;

22
int main( )
{
Matrix m(10,10);
int i, j;

for(i=0; i<3; i++)


for(j=0; j<4; j++)
cin >> m(i, j);

for(i=0; i<3; i++)


{
for(j=0; j<4; j++)
cout << m(i, j) << " ";
cout << endl;
}

return 0;
}

Example: Define a class Circle with private data members radius, area of type float, and large of type bool.
The data member large is true if the area is more than 100, and false if the area is less than or equal to 100. The
class has the public member functions set(float), print(), findArea(), and a default parametrized constructor.

Overload the arrow operator -> and the logical and operator && as member functions.
Overload the comma operator , and the unary operator += as non-member functions.

Use const to inforce the least privileged principle.

Use the following driver:


int main()
{
Circle u, v;

(u -> set(11.1), u -> print(), v -> set(3.8), v -> print());

(u += v, u.print());

if(u && v)
cout << "The two object are of the same type" << endl;
else
cout << "The two object are of different type" << endl;

23
return 0;
}
Sample output:

// Circle.h

#include <iostream>
#include <cmath>

using namespace std;

class Circle
{
friend Circle operator,(const Circle&, float);
friend void operator+=(Circle&, const Circle&);
public:
void set(float);
void print() const;
void findArea();
Circle* operator->();
bool operator&&(const Circle&) const;
Circle(float = 0);

private:
float radius, area;
bool large;
};

// CircleImp.cpp

#include "Circle.h"

const float pi = 4*atan(1.0);

void Circle::set(float r)
{
radius = r;

findArea();
}

void Circle::print() const


{
cout << "The radius is " << radius << " and the area is " << area << "; the circle is ";
if(large)

24
cout << "large" << endl;
else
cout << "is not large" << endl;
}

void Circle::findArea()
{
area = pi*radius*radius;

if(area > 100)


large = true;
else
large = false;
}

Circle::Circle(float r)
{
radius = r;

findArea();
}

Circle* Circle::operator->()
{
return this;
}

bool Circle::operator&&(const Circle &Obj) const


{
return large && Obj.large;
}

void operator+=(Circle &ObjL, const Circle &ObjR)


{
ObjL.radius += ObjR.radius;
ObjL.findArea();
}

Circle operator,(const Circle &Obj, float r)


{
return Obj;
}

Overloading the Assignment Operator (=)


One of the built-in operations on classes is the assignment operation. The assignment operator causes a
member-wise copy of the data members of the class. For example, the statement:
myRectangle = yourRectangle;

25
is equivalent to the statements:
myRectangle.length = yourRectangle.length;
myRectangle.width = yourRectangle.width;
The built-in assignment operator works well for classes that do not have pointer data members, but not
for classes with pointer data members. Therefore, to avoid the shallow copy of data for classes with
pointer data members; we must explicitly overload the assignment operator. Recall that to overload the
assignment operator = for a class, the operator function operator= must be a member of that class.

General Syntax to Overload the Assignment Operator = for a Class


The general syntax to overload the assignment operator = for a class is described next.

Function Prototype (to be included in the defmition of the class):


const className& operator=(const className&);

Function Definition:

const className& className::operator=(const className& rightObject)


{
// local declaration, if any
if (this != &rightObject) // avoid self-assignment
{
// algorithm to copy rightObject into this object
}
// Return the object aasigned.
return *this;
}

In the definition of the function operator=:


 There is only one formal parameter.
 The formal parameter is usually a const reference to a particular class.
 The function return type is a constant reference to a particular class.

We now explain why the return type of the function operator= should be a reference of the class type.
Suppose that the assignment operator = is overloaded for the class rectangle. The statement:
my Rectangle = your Rectangle;

is equivalent to the statement:


myRectangle.operator=(yourRectangle);
That is, the object yourRectangle becomes the actual parameter to the function operator=.

Now consider the statement:


myRectangle = yourRectangle = tempRect;
Because the associativity of the operator = is from right to left, this statement is equivalent to the

26
statement:
myRectangle.operator=(yourRectangle.operator=(tempRect));

Clearly, we must first execute the expression:


yourRectangle.operator=(tempRect)

that is, the expression:


yourRectangle = tempRect
The value returned by the expression:
yourRectangle.operator=(tempRect)
will become the parameter to the function operator= in order to assign a value to object myRectangle.
Because the formal parameter of function operator= is a reference parameter, the expression:
yourRectangle.operator=(tempRect)
must return a reference to the object, rather than its value. That is, it must return a reference to the
object yourRectangle, not the value of yourRectangle. For this reason, the return type of the function
to overload the assignment operator = for a class must be a reference to the class type.

Now consider the statement:


myRectangle = myRectangle;
Here we are trying to copy the value of myRectangle into myRectangle; that is, this statement is a self-
assignment. One reason why we must prevent such assignments is because they waste computer time.
First, however, we explain how the body of the assignment operator prevents such assignments.
As noted above, the body of the function operator=( ) does prevent assignments such as the one given
above. Let us see how. Consider the if statement in the body of the operator function operator=( ):

if (this != &rightObject) // avoid self-assignment


{
// algorithm to copy rightObject into this object
}

The statement:
myRectangle = myRectangle;

is compiled into the statement:


myRectangle.operator=(myRectangle);

Because the function operator=( ) is invoked by the object myRectangle, the pointer this in the body
of the function operator=( ) refers to the object myRectangle. Furthermore, because myRectangle is
also a parameter of the function operator=( ), the formal parameter rightObject also refers to the
object rnyRectangle. Therefore, in the expression:
this != &rightObjec
this and &rightObject both mean the address of myRectangle. Thus, the expression will evaluate to
false and, therefore, the body of the if statement will be skipped.

27
Example: Consider the following example

class arrayClass
{
public:
const arrayClass& operator=(const arrayClass&);
.
.
.

private:
int *list, length, maxSize;
};

The class arrayClass has a pointer data member, list, which is used to create an array to store integers.
Suppose that the definition of the function to overload the assignment operator for the class arrayClass
is written without the if statement, as follows:

const arrayClass & arrayClass::operator=(const arrayClass& otherList)


{
maxSize = otherList.maxSize;
length = otherList.length;

delete [ ] list;
list = new int[maxSize];
assert(list != NULL);

for(int i = 0; i < length; i++)


list[i] = otherList.list[i];

return *this;
}

Suppose that we have the following declaration in a user program:


arrayClass myList;
Consider the following statement.
myList = myList;
This is a self-assignment.

It follows that the definition of the function operator= must prevent self-assignments. The correct
definition of operator= for the class arrayClass is:

const arrayClass & arrayClass::operator=(const arrayClass& otherList)


{
if(this != &otherList)

28
{
delete [ ] list;
maxSize = otherList.maxSize;
length = otherList.length;

list = new int[maxSize];


assert(list != NULL);

for(int i = 0; i < length; i++)


list[i] = otherList.list[i];
}

return *this;
}

Overloading Unary Operators


The process of overloading unary operators is similar to the process of overloading binary operators.
The only difference is that in the case of binary operators, the operator has two operands. In the case
of unary operators, the operator has only one argument. Therefore, to overload a unary operator for a
class:
 If the operator function is a member of the class, it has no parameters.
 If the operator function is a nonmember-that is, a friend function of the class-it has one
parameter.
Overloading the Increment (++) and Decrement (--) Operators
The increment operator has two forms: pre-increment (++u) and post-increment (u++), where u is a
variable, say, of the type int. In the case of pre-increment, ++u, the value of the variable, u, is
incremented by 1 before the value of u is used in an expression. In the case, of post-increment, the
value of u is used in the expression before it is incremented by 1.

Overloading the Pre-Increment Operator


Overloading the pre-increment operator is quite straightforward. In the function definition, first we
increment the value of the object and then use the pointer this to return the object's value.

For example, suppose that we overload the pre-increment operator for the class rectangle to increment
the length and width of a rectangle by 1. Also, suppose that the operator function operator++( ) is a
member of the class rectangle. The operator function operator++( ) then has no arguments. Because
the operator function operator++( ) has no arguments, we use the pointer this to return the
incremented va1ue of the object.

rectangle rectangle::operator++( )
{
// increment the length and width
++length;
++width;

return *this; // return the incremented value of the object

29
}

Because myRectangle is an object of the type rectangleType, the statement:


++myRectangle;
increments the values of the length and width of myRectangle by 1. Moreover, the pointer this
associated with myRectangle returns the incremented value of myRectangle, which is ignored.

Now yourRectangle is also an object of the type rectangleType and so the statement:
yourRectangle = ++myRectangle;
increments the length and width of myRectangle by 1, and the pointer this associated with
myRectangle returns the incremented value of myRectangle, which is copied to yourRectangle.

General Syntax to Overload the Pre-Increment Operator ++ as a Member Function


The general syntax to overload the pre-increment operator ++ as a member function is described next.

Function Prototype (to be included in the definition of the class):


className operator++( );

Function Definition:

className className::operator++( )
{
// increment the value of the object by 1 (i.e.: each data member by 1)
return *this;
}

The operator function to overload the pre-increment operator can also be a nonmember of the class
rectangle, which we describe next.
Because the operator function operator++ is a nonmember function of the rectangle, it has one
parameter, which is an object of the type rectangle.

rectangle operator++(rectangle& rectObj)


{
// increment the length and width
++( rectObj.length);
++( rectObj.width);

return rectObj; // return the incremented value of the object


}

General Syntax to Overload the Pre-Increment Operator ++ as a Nonmember Function

30
The general syntax to overload the pre-increment operator ++ as a nonmember function is described
next.

Function Prototype (to be included in the definition of the class):


friend className operator++(className&);

Function Definition:

className operator++( className& incObj)


{
// increment the value of incObj by 1 (i.e.: each data member by 1)
return incObj;
}

Overloading the post-Increment Operator


We now discuss how to overload the post-increment operator. As in the case of the pre-increment
operator, we first describe the overloading of this operator as a member of a class.
Let us overload the post-increment operator for the class rectangle. In both cases, pre- and post-
increment, the name of the operator function is the same, i.e. operator++( ). To distinguish between
pre- and post-increment operator overloading, we use a dummy parameter (of the type int) in the
function heading of the operator function. Thus, the function prototype for the post-increment operator
of the class rectangle is:
rectangle operator++(int);

The statement:
myRectangle++;

is compiled by the compiler in the statement:


myRectangle.operator++( 0 );
and so the function operator++( ) with a parameter executes. The parameter 0 is used merely to
distinguish between the pre- and post-increment operator functions.
The post-increment operator first uses the value of the object in the expression, and then increments
the value of the object. So the steps required to implement this function are:
 Save the value of the object-in say, temp.
 Increment the value of the object.
 Return the value that was saved in temp.
The function definition of the post-increment operator for the class rectangle is:

rectangle rectangle::operator++(int u)
{
Rectangle temp = *this; // use this pointer to copy the value of the object

length++;
width++;

31
return temp; // return the old value of the object
}

General Syntax to Overload the Post-Increment Operator ++ as a Member Function


The general syntax to overload the post-increment operator ++ as a member function is described next.

Function Prototype (to be included in the definition of the class):


className operator++(int);
Function Definition:

className className::operator++(int u)
{
className temp = *this; // use this pointer to copy the value of the object

// increment the object

return temp; // return the old value of the object


}

The post-increment operator can also be overloaded as a nonmember function of the class. In this
situation, the operator function operator++ has two parameters. The definition of the function to
overload the post-increment operator for the class rectangleType as nonmember is:

rectangle operator++(rectangle& rectObj, int u)


{
rectangle temp = rectObj; // copy rectangle into temp
// increment the length and width of rectangle
(rectObj.length)++;
(rectObj.width)++;

return temp; // return the old value of the object


}

General Syntax to Overload the Post-Increment Operator ++ as a Nonmember Function


The general syntax to overload the post-increment operator ++ as a nonmember function is described
next.

Function Prototype (to be included in the definition of the class):


friend className operator++(className&, int);

Function Definition:

className operator++( className& incObj, int u)


{

32
className temp = incObj; // copy incObj into temp

// increment incObj

return temp; // return the old value of the object


}

The decrement operator can be overloaded in a similar way.

Function Overloading
Operator overloading provides the programmer with the same concise notation for user-defined data
types as the operator has for built-in types. The types of arguments used with an operator determine
the action to take. Similar to operator overloading, C++ allows the programmer to overload a function
name.
Recall that a class can have more than one constructor, but all constructors of a class have the same
name, which is the name of the class. This is an example of overloading a function. Further recall that
overloading a function refers to having several functions with the same name, but different parameters.
The types of parameters determine which function will execute.

For function overloading to work, we must give the definition of each function. The next
section teaches you how to overload functions with a single code segment and leave the job of
generating code for separate functions for the compiler.

Templates
Templates are a very powerful feature of C++. They allow you to write a single code segment for both
a set of related functions, called a function template, and for a set of related classes, called a class
template. The syntax we use for templates is:
template<class Type>
declaration;
where Type is the name of a data type, built-in or user-defined, and declaration is either a function
declaration or a class declaration. In C++, template is a reserved word.

The word class in the heading refers to any user-defined type or built-in type. Type is referred to as a
formal parameter to the template.
Similar to variables being parameters to functions, types (that is, data types) are parameters to
templates.

Function Templates
Earlier in this chapter, we introduced the function larger to find the larger of two integers, characters,
double numbers, or strings. To implement the function larger, we need to write four function
definitions for the data type: one for int, one for char, one for double, and one for string. However,
the body of each, function is similar.

C++ simplifies the process of overloading functions by providing function templates. The syntax of

33
the function template is:
template<class Type>
function definition;
where Type is referred to as a formal parameter of the template. It is used to specify the type of
arameters to the function and the return type of the function, and to declare variables within the
function.

The statements:
template<class Type>
Type larger(Type x, Type y)
{
if (x >= y)
return x:
else
return y;
}
define a function template larger, which returns the larger of two items. In the function heading, the
type of the formal parameters x and y is Type, which will be specified by the type of the actual
parameters when the function is called. The statement:
cout << larger(5, 6) << endl;
is a call to the function template larger. Because 5 and 6 are of the type int, the data type int is
substituted for Type and the compiler generates the appropriate code. If we omit the body of the
function in the function template definition, the function template, as usual, is the prototype.

// Function overloading char larger(char a, char b)


#include <iostream> {
#include <string> if(a >= b)
return a;
using namespace std; else
return b;
int larger(int x, int y); }
char larger(char first, char second);
double larger(double u, double v);
string larger(string a, string b);
double larger(double a, double b)
int main( )
{
{
if(a >= b)
cout << larger(3, 6) << endl;
return a;
cout << larger('A', 'a') << endl;
else
cout << larger(7.1, 3.6) << endl;
return b;
cout << larger("Omar", "Ali") << endl;
}
return 0;
}
int larger(int a, int b) string larger(string a, string b)
{ {
if(a >= b) if(a >= b)

34
return a; return a;
else else
return b; return b;
} }

Sample Run:
6
a
7.1
Omar

The following example illustrates the use of function templates.

#include <iostream>
using namespace std;

template <class Type>


Type larger(Type x, Type y);

int main( )
{
cout << larger(3, 6) << endl;
cout << larger('A', 'a') << endl;
cout << larger(7.1, 3.6) << endl;
cout << larger("Omar", "Ali") << endl;
return 0;
}
template<class Type>
Type larger(Type x, Type y)
{
if (x >= y)
return x;
else
return y;
}

Sample Run:
6
a
7.1
Omar

Class Templates
Like function templates, class templates are used to write a single code segment for a set of related
classes. For example, if we defined a list as an ADT; and assume that our list element type was int,
then if the list element type changes from int to, say, char, double, or string, then we need to write
separate classes for each element type. For the most part, the operations on the list and the algorithms
to implement those operations remain the same. Using class templates, we can create a generic class
listType, and the compiler can generate the appropriate source code for a specific implementation.

35
The syntax we use for a class template is:
template<class Type>
class declaration
Class templates are called parameterized types because, based on the parameter type, a specific class is
generated.

The following statements define listType to be a class template:

template <class elemType>


class listType
{
public:
void sort( );
void print( ) const;
void insertAt(const elemType& item, int position);
listType(int listSize = 50);
~listType( );
private:
int maxSize, length;
elemType *list;
};

The defintion of the class templeat and the definitions of the member functions must be in the same
file (say, the header file).

Example:

#ifndef H_listType
#define H_listType

#include <iostream>
#include <cassert>

using namespace std;

template <class elemType>


class listType
{
public:
void sort( );
void print( ) const;
void insertAt(const elemType& item, int position);
listType(int listSize = 50);
~listType( );
private:
int maxSize, length;
elemType *list;
};

36
//constructor; the default array size is 50
template<class elemType>
listType<elemType>::listType(int listSize)
{
maxSize = listSize;
length = 0;
list = new elemType[maxSize];
assert(list != NULL);
}

template<class elemType>
listType<elemType>::~listType( ) //destructor
{
delete [ ] list;
}

template<class elemType>
void listType<elemType>::sort( ) //selection sort
{
int i, j, min;
elemType temp;

for (i = 0; i < length; i++)


{
min = i;
for (j = i+1; j < length; ++j)
if (list[j] < list[min])
min = j;
temp = list[i];
list[i] = list[min];
list[min] = temp;
}//end for
}//end sort

template<class elemType>
void listType<elemType>::print( ) const
{
int i;
for (i = 0; i < length; ++i)
cout << list[i] << " ";
cout << endl;
}//end print

template<class elemType>
void listType<elemType>::insertAt(const elemType& item, int position)
{
assert(position >= 0 && position < maxSize);
list[position] = item;
length++;

37
}
#endif

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

using namespace std;

int main( )
{
listType<int> intList(100);
listType<double> doubleList(100);

int index, number;

cout << "Processing the integer list" << endl;


cout << "Enter 5 integers: ";

for (index = 0; index < 5; index++)


{
cin >> number;
intList.insertAt(number, index);
}
cout << endl;
cout << "The list you entered is: " << endl;
intList.print( );
cout << endl;
cout << "After sorting, the list is: " << endl;
intList.sort( );
intList.print( );
cout << endl;

double decimal;

cout << "Processing the double list" << endl;

cout << "Enter 5 decimals: ";

for (index = 0; index < 5; index++)


{
cin >> decimal;
doubleList.insertAt(decimal, index);
}

cout << endl;


cout << "The list you entered is: " << endl;
doubleList.print( );
cout << endl;

cout << "After sorting, the list is: " << endl;

38
doubleList.sort( );
doubleList.print( );
cout << endl;

int intListSize;

cout << "Enter the size of the integer list: ";


cin >> intListSize;

listType<int> intList2(intListSize);

cout << "Processing the integer list" << endl;


cout << "Enter " << intListSize << " integers: ";

for (index = 0; index < intListSize; index++)


{
cin >> number;
intList2.insertAt(number, index);
}

cout << endl;


cout << "The list you entered is: " << endl;
intList2.print( );
cout << endl;
cout << "After sorting, the list is: " << endl;
intList2.sort( );
intList2.print( );
cout << endl;

return 0;
}

Summary of Chapter 15, page 801 from the book


C++ Programming: From Problem Analysis to Program Design
By: D.S. Malik
2nd edition, 2004

39
Thomson Course Technology
ISBN: 0-619-16042-X

Go back to the top

40

You might also like