Professional Documents
Culture Documents
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.
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.
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
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.
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.
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.
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.
};
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.
}
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.
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;
}
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( );
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.
aObject.setX(32);
two(aObject);
return 0;
}
Sample Run
In class classIllusFriend: x = 45
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;
};
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.
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
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.
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
// rectangleImp.cpp
#include <iostream>
#include "rectangle.h"
if ( w >= 0 )
width = w;
else
width = 0;
}
9
void rectangle::print( ) const
{
cout << "Length = " << length << " Width = " << width;
}
rectangle::rectangle( )
{
length = width = 0;
}
return tempRect;
}
return tempRect;
}
return tempRect;
}
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"
int main( )
{
rectangle rectangle1(23, 45);
rectangle rectangle2(12, 10);
rectangle rectangle3;
rectangle rectangle4;
rectangle rectangle5;
rectangle rectangle6;
11
cout << "rectangle5: ";
rectangle5.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.
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.
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:
return tempRect;
}
To include the operator function operator==( ) as a nonmember function of the class rectangle, its
prototype in the definition of rectangle is:
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.
Function Definition:
ostream& operator<<(ostream& osObject, const className& cObject)
{
// Local declaration, if any
// Output the members of cObject.
// osObject << . . .
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.
Function Definition:
15
// Return the stream object.
return isObject;
}
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;
return osObject;
}
// 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"
void sales::get(string& IDSalesPerson, int& radioItem, int& televisionItem, int& computerItem) const
{
IDSalesPerson = ID;
radioItem = radio;
televisionItem = television;
computerItem = computer;
}
17
sales sales::operator+(const sales& salesObject) const
{
sales tempSales;
return tempSales;
}
return osObject;
}
return isObject;
}
// sales.cpp
#include <iostream>
#include <string>
#include "sales.h"
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.
cout << "Enter values for the three objects, an ID and three integers for each object" << endl;
cin >> one >> two >> three;
cout << one << two << three;
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)
return osObject;
}
19
cObject.set(sID, r, t, c);
return isObject;
}
Example:
// array.h
#include <string>
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"
Array::Array(int vLength)
{
length = vLength;
20
p = new int [length];
Array::~Array( )
{
delete [ ] p;
p = NULL;
}
return p[index];
}
// array.cpp
#include <iostream>
#include <string>
#include "array.h"
int main( )
{
Array u(5);
u.set(5);
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"
Matrix::~Matrix( )
{
delete [ ] data;
data = NULL;
}
/* driver program */
#include <iostream>
#include "header.h"
22
int main( )
{
Matrix m(10,10);
int i, j;
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.
(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>
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"
void Circle::set(float r)
{
radius = r;
findArea();
}
24
cout << "large" << endl;
else
cout << "is not large" << endl;
}
void Circle::findArea()
{
area = pi*radius*radius;
Circle::Circle(float r)
{
radius = r;
findArea();
}
Circle* Circle::operator->()
{
return this;
}
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.
Function Definition:
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;
26
statement:
myRectangle.operator=(yourRectangle.operator=(tempRect));
The statement:
myRectangle = 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:
delete [ ] list;
list = new int[maxSize];
assert(list != NULL);
return *this;
}
It follows that the definition of the function operator= must prevent self-assignments. The correct
definition of operator= for the class arrayClass is:
28
{
delete [ ] list;
maxSize = otherList.maxSize;
length = otherList.length;
return *this;
}
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;
29
}
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.
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.
30
The general syntax to overload the pre-increment operator ++ as a nonmember function is described
next.
Function Definition:
The statement:
myRectangle++;
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
}
className className::operator++(int u)
{
className temp = *this; // use this pointer to copy the 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:
Function Definition:
32
className temp = incObj; // copy incObj into temp
// increment incObj
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.
34
return a; return a;
else else
return b; return b;
} }
Sample Run:
6
a
7.1
Omar
#include <iostream>
using namespace std;
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 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>
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;
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"
int main( )
{
listType<int> intList(100);
listType<double> doubleList(100);
double decimal;
cout << "After sorting, the list is: " << endl;
38
doubleList.sort( );
doubleList.print( );
cout << endl;
int intListSize;
listType<int> intList2(intListSize);
return 0;
}
39
Thomson Course Technology
ISBN: 0-619-16042-X
40