Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                

Chapter XXVIII

Download as doc, pdf, or txt
Download as doc, pdf, or txt
You are on page 1of 65

Chapter XXVIII

Focus on Object Oriented Programming


Overloaded Operators

Chapter XXVIII Topics


28.1 Introduction

28.2 Setting the Stage with Piggy

28.3 Unary Operators

28.4 Overloading Operator Nono

28.5 Arithmetic Assignment Operators

28.6 Creating a Power Operator

28.7 Return Operator Functions

28.8 Binary Operators

28.9 Comparison Operators

28.10 Assignment Operators

28.11 Free Operator Functions

28.12 Insertion and Extraction Operators

28.1 Introduction

Chapter XXVIII Overloaded Operators 28.1


The last two chapters have focused heavily on the first main feature of Object
Oriented Programming, encapsulation. Now it is time to investigate
polymorphism and learn the powerful programming capabilities that it brings.
Do you remember the meaning of polymorphism? How about a little reminder?

Polymorphism Definition

Poly-morphism means many forms.

Polymorphism allows a single accessing feature, such as an


operator or function identifier, to have many forms.

A function or operator is said to be overloaded, when it


has more than one form.

When you think back on the last two chapters you realize that some overloaded
aspects were already used. The focus of the last two chapters was on member
functions in general and constructors and destructors specifically. In the process
of detailing constructor features we did look at overloaded constructors. Well,
such is the normal course of events in computer science. Try, as you can to
follow the beautiful elegance of a mathematical sequence of topics, and you will
get bitten. Computer science is too interrelated. However, that is fine because
these chapters are all titled Focus on Object Oriented Programming followed by
the particular topic of the focus.

The OOP topic of this chapter is overloaded operators. The chapter is not called
polymorphism because overloaded operators are a subset of the larger topic of
polymorphism. Overloaded operators bring exciting programming power to the
C++ language and is one major reason for the popularity of C++.

In the first few computer science chapters with C++, you used overloaded
operators without knowing or caring. One common example is the use of the plus
( + ) operator with the int type to perform arithmetic addition, such as the
statement Sum = Number1 + Number2. The same plus operator with totally
different results occurs with string concatenation in a program statement, such as
FullName = FirstName + LastName.
Perhaps the best way to demonstrate the difference is with these two plus operator
examples, which look almost identical but have totally different results.

Chapter XXVIII Overloaded Operators 28.2


Arithmetic Addition: 100 + 200 = 300

String Concatenation: ”100” + ”200” = ”100200”

Just about any language has such features that various operators are used for
different procedures, and the procedure selected depends on the context of the
operator. A plus operator placed between two integer types performs arithmetic
addition, and the same plus operator placed between two strings performs string
concatenation.

What makes a computer programming language with modern object oriented


programming - like overloaded operators - different is not that overloaded
operators exist, but they can be created. Yes, that is right, you, the programmer
can create your very own operator functions with capabilities custom-tailored to
some very specialized needs.

For example, matrix multiplication is a rather involved process of manipulating a


set of two-dimensional arrays with three nested loops to get the product matrix. It
is certainly possible to create a Multiplication function that takes care of this job
along with three parameters. You would get a statement something like:

MatrixMultiplication(M1,M2,M3);

With overloaded operators it is possible to create a matrix class and implement a


multiplication operator, the asterisk, in this class to perform the complex matrix
multiplication routine. The end result is that with M1, M2 and M3 objects of the
Matrix class, the following statement makes perfect sense:

M3 = M1 * M2;

In mathematical and statistical programs there are numerous examples where a


program benefits in readability and conciseness by the ability to make operators
perform precisely in the manner that is mathematically correct. Overloaded
operators are not limited to mathematical use, but the heavy use of operators by
the mathematical discipline makes that field a natural for this C++ feature. The
purpose of this chapter is to learn how to add overloaded operators to user
declared classes and take control of this powerful OOP capability.

28.2 Setting the Stage with Piggy

Chapter XXVIII Overloaded Operators 28.3


You need to clearly understand something about overloaded operators before we
even get started with some program examples. Overloaded operators only have
meaning in connection with some class. In other words, we do not decide to turn
the > operator into some type of exponentiation operator for any location in our
program. That is not how this business works. You or any other programmer
start with some class. This class has attributes and actions, and these actions can
include special functions that are operator functions.

This should make a lot of sense. Earlier I mentioned that it is possible to have a
statement like M3 = M1 * M2; multiply two matrices by overloading the asterisk
operator. Now this only works because M1, M2 and M3 are objects of a special
class that declares a special multiply operator.

Fundamental Overloaded Operator Concept

C++ allows certain operators to be used for different,


user-created functionalities.

Indicating that an operator, like + or any other operator,


is to be used in a manner that is different from the original
C++ functionality is called overloading the operator.

Using an overloaded operator is possible within the context


of an object that includes the overloaded operator as part
of its class declaration.

How about putting this all in simple English? Well here it goes. Create all the
overloaded operators you desire. Explain with proper C++ syntax how these
operators are supposed to behave inside some class. Whenever you use your nifty
operator make sure that it is done with objects of the class that you declared.

The Piggy(bank) Class

This entire chapter will use the same basic class to explain a variety of overloaded
operators. It is the PiggyBank class, abbreviated to Piggy. This is a very simple

Chapter XXVIII Overloaded Operators 28.4


class with few attributes and actions. Throughout the chapter there will be
changes in the class as different features of overloaded operators are
demonstrated. Fundamentally, the class is unchanged in its main purpose.
Objects of the Piggy class keep track of some person’s savings. Savings is a
private attribute of the class and various functions will manipulate those savings.

Program PROG2801.CPP shows the fundamental Piggy class. It has one private
Savings attribute to store the total amount saved. There is a default constructor
that initializes Savings to $10.00, and a ShowSavings member function to display
the total amount saved in the piggybank.

// PROG2801.CPP
// This program declares the minimum Piggy class.
// The Piggy(bank) class will be used throughout the
// chapter to demonstrate overloaded operator concepts.

#include <iostream.h>
#include <conio.h>

class Piggy
{
private:
float Savings;
public:
Piggy()
{ Savings = 10; }
void ShowSavings() const
{ cout << Savings << " dollars saved" << endl; }
};

void main()
{
clrscr();
Piggy Tom;
Tom.ShowSavings();
getch();
}

PROG2801.CPP OUTPUT

10 dollars saved

The Piggy class lacks any input ability to alter the amount that is saved. The
constructor has no parameters, and Savings is private so you cannot do anything
to alter the value of $10.00 in the piggybank. This shortcoming is remedied in the

Chapter XXVIII Overloaded Operators 28.5


next program by adding the Increment member function, which increases savings
by $1.00 dollar each time that the Increment function is called.

// PROG2802.CPP
// This program adds an Increment function to the Piggy class.
// Increment increases the savings by one dollar.

#include <iostream.h>
#include <conio.h>

class Piggy
{
private:
float Savings;
public:
Piggy()
{ Savings = 10; }
void ShowSavings() const
{ cout << Savings << " dollars saved" << endl; }
void Increment()
{ Savings++; }
};

void main()
{
clrscr();
Piggy Tom;
Tom.ShowSavings();
Tom.Increment();
Tom.ShowSavings();
getch();
}

PROG2802.CPP OUTPUT

10 dollars saved
11 dollars saved

Chapter XXVIII Overloaded Operators 28.6


Have you seen any exciting overloaded operator stuff yet? Absolutely nothing.
All that you have seen is a Piggy class and a few programs that use the class.
You did see how Savings can be altered with a special Increment function.

Now think of the usual mathematical type operations. If you have a variable like
int X; do you call some special function to alter its value? No, what you do is
use some operators like X++ or X+=10. This very convenient operator capability
is what we aim to achieve with an object like Tom of our Piggy class. Rather
than using a statement like Tom.Increment(), we want to use ++Tom to increase
the savings of the piggybank or we want to use Tom++. These types of
operations is what will be added to Piggy in the rest of this chapter.

28.3 Unary Operators

Program PROG2803.CPP, on the next page, will seem plenty weird. It is almost
a copy of the previous program, and it does have the same output. The previous
Increment function has now been replaced by an operator++ function. Aside
from the strange function identifier, everything is the same. Running the program
will verify that the syntax is correct and that the execution is identical to the
previous program. But since when are you allowed to use ++ characters in a
function identifier? Also - for those looking at a monitor - why is the identifier
operator, displayed in bold?

By now you should know that bold indicates a C++ reserved word. You are
observing the oddity that a function name is used that appears to be a reserved
word, and C++ is happy with this arrangement.

In just a moment some special use will be made of this operator keyword, but for
now the point is that a member function of an object, regardless of its name, can
be called using the standard dot notation of an object.

Note, in the main function that the statement Tom.operator++() uses the precise
same syntax as any other void public member function. The point is that the
operator functions that you will see in this chapter, are member functions, even if
they behave differently.

// PROG2803.CPP
// This program substitutes the Increment function with an

Chapter XXVIII Overloaded Operators 28.7


// operator++ function, using "dot function" notation.

#include <iostream.h>
#include <conio.h>

class Piggy
{
private:
float Savings;
public:
Piggy()
{ Savings = 10; }
void ShowSavings() const
{ cout << Savings << " dollars saved" << endl; }
void operator++()
{ Savings++; }
};

void main()
{
clrscr();
Piggy Tom;
Tom.ShowSavings();
Tom.operator++();
Tom.ShowSavings();
getch();
}

PROG2803.CPP OUTPUT

10 dollars saved
11 dollars saved

The intention of overloaded operators is not to use the regular function syntax,
like the previous program, but use an abbreviated syntax that simulates the
manner in which operators are normally used. In this section we start by looking
at the overloaded unary operators.

Unary operators are a good starting point. A unary operator only has one operand
that makes life simpler at this stage. Our concern is how to substitute the earlier
Increment function with something that will make an object of the Piggy class
increase Savings with a statement like ++Tom. This task may seem rather
perplexing if it were not for the C++ operator reserved word. C++ has made this
overloading business rather simple. You want to give special meaning to the ++
operator? Fine, create a void function called operator++ and explain in the body
of the function what needs to be executed.

Chapter XXVIII Overloaded Operators 28.8


C++ operator Reserved Word

The C++ reserved keyword operator is the backbone of


overloaded operator syntax. Special operator functions
are declared using a function identifier that is a combination
of the word operator and the operator that is overloaded.
void operator++()
void operator--()
void operator+=()

Program PROG2804.CPP uses the exact same Piggy class with the same
operator++ function as the previous program. The only difference is that the
normal dot.function notation style of member function calling is replaced with an
overloaded operator notation style. Only the main function is shown below
where you see the different function calling style.

// PROG2804.CPP
// This program substitutes the Increment function with an
// operator++ function, which accomplishes the same thing.

void main()
{
clrscr();
Piggy Tom;
Tom.ShowSavings();
++Tom; // before it was Tom.operator++();
Tom.ShowSavings();
getch();
}

PROG2804.CPP OUTPUT

10 dollars saved
11 dollars saved

Chapter XXVIII Overloaded Operators 28.9


The object Tom is intentionally incremented with the prefix notation ++Tom. C+
+ assumes that the operator++ function will be invoked with prefix notation.
Some other notation is required before C++ recognizes postfix function calls.

The problem with postfix notation is illustrated in the next program. The same
operator++ function is called using both prefix and postfix notation. The
program does compile, and it does execute. However, C++ objects strongly with
a warning message after compiling.

// PROG2805.CPP
// This program calls the operator++ function using both
// prefix and postfix notation. The postfix notation
// causes a warning.

#include <iostream.h>
#include <conio.h>

class Piggy
{
private:
float Savings;
public:
Piggy()
{ Savings = 10; }
void ShowSavings() const
{ cout << Savings << " dollars saved" << endl; }
void operator++()
{ Savings++; }
};

void main()
{
clrscr();
Piggy Tom;
Tom.ShowSavings();
++Tom;
Tom.ShowSavings();
Tom++;
Tom.ShowSavings();
getch();
}

WARNING MESSAGE

Warning PROG2805.CPP 32: Overloaded prefix ’operator’


used as
a post fix operator

Chapter XXVIII Overloaded Operators 28.10


PROG2805.CPP OUTPUT

10 dollars saved
11 dollars saved
12 dollars saved

The examples shown so far only incremented one data field, the Savings attribute.
It may seem natural that the ++ operator only increments one variable. There is
no such restriction in C++ and it is entirely possible to alter the values of multiple
attributes. Program PROG2806.CPP adds a Weight attribute to the Piggy class.
This new attribute stores the weight, in ounces, of the piggybank. This example
does not use the inline operator function, shown in earlier examples. This time
the operator function uses only the prototype inside the class, and the function
implementation is detailed later.

// PROG2806.CPP
// This program demonstrates that an operator function can alter
// more than one attribute. In this program the Piggy class also
// has a Weight attribute, which measures the weight of the
// Piggy(bank) in ounces.

#include <iostream.h>
#include <conio.h>

class Piggy
{
private:
float Savings;
int Weight;
public:
Piggy();
void ShowSavings() const;
void operator++();
};

void main()
{
clrscr();
Piggy Tom;
Tom.ShowSavings();
++Tom;
Tom.ShowSavings();
getch();
}

Chapter XXVIII Overloaded Operators 28.11


Piggy::Piggy()
{
Savings = 10;
Weight = 50;
}

void Piggy::ShowSavings() const


{
cout << endl;
cout << Savings << " dollars saved" << endl;
cout << "Weight is " << Weight << " ounces" << endl;
}

void Piggy::operator++()
{
Savings += 10;
Weight += 25;
}

PROG2806.CPP OUTPUT

10 dollars saved
Weight is 50 ounces

20 dollars saved
Weight is 75 ounces

This program example helps to explain several concepts that perhaps you took for
granted or had not understood. Notice that the operator++ not only increments
two different attributes with the same call, but the function also increments the
attributes with different amounts. You may have assumed that the ++ operator
would automatically increment by a value of one. Such beliefs are from using
integer variables like K++. In that case you are using the C++ provided
incrementer. When you declare an operator function, you have the choice to
increment any way that seems appropriate.

Incorrect operator++ Assumptions

++ is not limited to incrementing one class attribute.


Many attributes can be altered.

Chapter XXVIII Overloaded Operators 28.12


++ is not restricted to incrementing an attribute by 1,
but can alter an attribute by any indicated amount.

Program PROG2807.CPP is a slight alteration of the previous program


examples. This time the Piggy class is declared with two operator functions, one
for incrementing and another one for decrementing.

// PROG2807.CPP
// This program demonstrates two different operator functions.

#include <iostream.h>
#include <conio.h>

class Piggy
{
private:
float Savings;
public:
Piggy()
{ Savings = 25; }
void operator++()
{ Savings++; }
void operator--()
{ Savings--; }
void ShowSavings() const
{ cout << Savings << " dollars saved" << endl; }

};

void main()
{
clrscr();
Piggy Tom;
Tom.ShowSavings();
++Tom;
Tom.ShowSavings();
--Tom;
Tom.ShowSavings();
getch();
}

PROG2807.CPP OUTPUT

Chapter XXVIII Overloaded Operators 28.13


25 dollars saved
26 dollars saved
25 dollars saved

You may be able to sleep very soundly knowing that your operator++ is assumed
to be a prefix operation by C++. On the other hand, you might wake up at weird
hours irritated that postfix does not gets its foot in the door. Well sleep on
because you can use postfix. The method may seem quite peculiar because it is
done by providing an int parameter. This is interesting because the heading of a
function helps C++ identify the appropriate overloaded function. We now have a
situation where we are using overloaded functions to properly implement
overloaded operators.

The appearance of a function prototype like void operator++(int); in the Piggy


class looks like the operator++ function uses some integer value for some
unknown purpose. This assumption is natural until you check the function
implementation and find that the integer parameter is used nowhere in the
function body.

This situation is a function with a special flag. C++ sees an operator++ heading
without any parameter and thinks that it a prefix operation. However if the same
function includes an int parameter, C++ knows that a postfix operation is
intended. This is a curious situation when a parameter does not pass a value nor
any type of reference, but exists for the purpose of identifying a function.

Prefix and Postfix ++ Operations

I was tempted to call this alert box when a parameter is


not a parameter.

Syntax for prefix operation:


void operator++();

Syntax for postfix operation:


void operator++(int);

Chapter XXVIII Overloaded Operators 28.14


The parameter int is not used for any value nor reference
passing. This parameter distinguishes the operator++
function from the non-parameter version.

Program PROG2808.CPP demonstrates two operator++ functions in the Piggy


class for prefix and postfix operations. You will note that this program compiles
and executes without any warnings or complaints from C++

// PROG2808.CPP
// This program demonstrates how an int parameter can flag an
// operator as a postfix operation.

#include <iostream.h>
#include <conio.h>

class Piggy
{
private:
float Savings;
public:
Piggy() { Savings = 25; }
void ShowSavings() const;
void operator++();
void operator++(int);
};

void main()
{
clrscr();
Piggy Tom;
Tom.ShowSavings();
++Tom;
Tom.ShowSavings();
Tom++;
Tom.ShowSavings();
getch();
}

void Piggy::ShowSavings() const


{
cout << endl;
cout << Savings << " dollars saved" << endl;
}

Chapter XXVIII Overloaded Operators 28.15


void Piggy::operator++()
{
cout << endl;
cout << "PREFIX OPERATOR" << endl;
Savings += 10;
}

void Piggy::operator++(int)
{
cout << endl;
cout << "POSTFIX OPERATOR" << endl;
Savings += 10;
}

PROG2808.CPP OUTPUT

25 dollars saved

PREFIX OPERATOR

35 dollars saved

POSTFIX OPERATOR

45 dollars saved

You may have thought that the only difference in the two operator++ functions is
the prefix and postfix calling emphasis. Aside from that distinction both
functions increment in the same manner. This is not a requirement. The next
program example shows that you can have two operator functions with the same
name, operator++, yet each function increments with different amounts.

// PROG2809.CPP
// This program shows that it is not necessary for the prefix
// and postfix operator functions to perform in the same manner.

#include <iostream.h>
#include <conio.h>

class Piggy
{
private:
float Savings;

Chapter XXVIII Overloaded Operators 28.16


public:
Piggy() { Savings = 25; }
void ShowSavings() const;
void operator++();
void operator++(int);
};

void main()
{
clrscr();
Piggy Tom;
Tom.ShowSavings();
++Tom;
Tom.ShowSavings();
Tom++;
Tom.ShowSavings();
getch();
}

void Piggy::ShowSavings() const


{
cout << endl;
cout << Savings << " dollars saved" << endl;
}

void Piggy::operator++()
{
cout << endl;
cout << "PREFIX OPERATOR" << endl;
Savings += 10;
}

void Piggy::operator++(int)
{
cout << endl;
cout << "POSTFIX OPERATOR" << endl;
Savings += 20;
}

PROG2809.CPP OUTPUT

25 dollars saved

PREFIX OPERATOR

35 dollars saved

POSTFIX OPERATOR

55 dollars saved

Chapter XXVIII Overloaded Operators 28.17


The object Tom has received a lot of attention, and frankly Tom is getting lonely.
Future program examples require that more than one object is defined in a
program. Multiple objects can be confusing and you may not quite know which
output belongs with which object. This problem is handled nicely by a new-and-
improved ShowSavings function. The new function uses a parameter to display
the object whose information is displayed.

Since we are brave enough to alter ShowSavings we can also add some color to
the Piggy constructor and use an int parameter to enter the amount of the initial
savings account. This is realistic. Not everybody starts their piggybank with the
same amount of cash. Program PROG2810.CPP shows these two improvements
along with a companion for Tom.

// PROG2810.CPP
// This program adds a parameter to the ShowSavings function to
// help identify the object. The constructor is also altered to
// specify an initial savings amount.

#include <iostream.h>
#include <conio.h>
#include "APSTRING.H"

class Piggy
{
private:
float Savings;
public:
Piggy(int);
void ShowSavings(apstring Name) const;
void operator++();
};

void main()
{
clrscr();
Piggy Tom(100);
Piggy Sue(200);
Tom.ShowSavings("Tom");
Sue.ShowSavings("Sue");
++Tom;
++Sue;
Tom.ShowSavings("Tom");

Chapter XXVIII Overloaded Operators 28.18


Sue.ShowSavings("Sue");
getch();
}

Piggy::Piggy(int Temp)
{
Savings = Temp;
}

void Piggy::ShowSavings(apstring Name) const


{
cout << endl;
cout << Name << " has " << Savings
<< " dollars saved" << endl;
}

void Piggy::operator++()
{
Savings += 10;
}

PROG2810.CPP OUTPUT

Tom has 100 dollars saved

Sue has 200 dollars saved

Tom has 110 dollars saved

Sue has 210 dollars saved

28.4 Overloaded Operator Nono

Chapter XXVIII Overloaded Operators 28.19


The flexibility of overloaded operators allows programmers to create some pretty
bizarre functions. It is possible to make the operator++ display a weird message
or compute some bizarre mathematical formula. To illustrate this point, the next
program uses the operator++ to decrease savings and the operator-- function to
increase savings in a Piggy object. C++ does not complain.

// PROG2811.CPP
// This program demonstrates that the performance of an operator
// function is not necessarily logically tied to its operator.

#include <iostream.h>
#include <conio.h>

class Piggy
{
private:
float Savings;
public:
Piggy()
{ Savings = 25; }
void operator++()
{ Savings--; }
void operator--()
{ Savings++; }
void ShowSavings() const
{ cout << Savings << " dollars saved" << endl; }

};

void main()
{
clrscr();
Piggy Tom;
Tom.ShowSavings();
++Tom;
Tom.ShowSavings();
--Tom;
Tom.ShowSavings();
getch();
}

PROG2811.CPP OUTPUT

25 dollars saved
24 dollars saved
25 dollars saved

Chapter XXVIII Overloaded Operators 28.20


Overloaded Operator Warning

The performance of an operator function is completely


depended on the code in the function body. There is no
C++ requirement that the logic of the function is connected
to the expected operation of the overloaded operator.

However it is a serious nono to write operator functions


that are counter-intuitive. ++Boo should be incrementing
something, not decrementing or multiplying values.

28.5 Arithmetic Assignment Operators

Arithmetic assignment operators are very popular operators in C++. They are the
shortcut notations where K += 12 is used to imply K = K + 12 or K *= 5 is a
shortcut for K = K * 5. This type of convenience would be very nice to add to
user defined classes and the operator function implements this very easily. The
logic is identical to the unary operator++ or operator-- functions. The primary
difference is that a parameter is necessary to pass the value that must be
incremented by the function. A typical operator+= looks as follows:

OPERATOR+= FUNCTION EXAMPLE

void operator+=(int Temp)


{

Chapter XXVIII Overloaded Operators 28.21


Attribute += Temp;
}

Program PROG2812.CPP demonstrates both operator+= and operator-=


functions. Notice how the main function uses these new overloaded operator
functions in the precise same manner as mathematical operations. In other words,
a statement like Tom += 300 increases Tom’s savings by $300.00 and a similar
statement like Sue -= 100 decreases Sue’s savings by $100.00. This is exactly the
same syntax that you have used in C++ for some time. You are not looking at
some amazing coincidence. C++ was designed with this type of capability in
mind. Overloaded operators add a lot of power to the C++ programmer.

Overloaded Operator Design not Coincidence

C++ was designed with the programmer in mind. The


inclusion of overloaded operators in object oriented
programming increases a programmer’s tool box considerably.

Furthermore, overloaded operators are implemented in the


same manner as the built in C++ operators. This makes
programming with overloaded operators, and reading
programs that use overloaded operators, very intuitive.

// PROG2812.CPP
// This program shows how to implement operator functions for
// += and -= as well as passing parameters to operator functions.

#include <iostream.h>
#include <conio.h>
#include "APSTRING.H"

class Piggy
{
private:
float Savings;
public:
Piggy(int);
void ShowSavings(apstring Name) const;
void operator+= (float Money) { Savings += Money; }
void operator-= (float Money) { Savings -= Money; }
};

Chapter XXVIII Overloaded Operators 28.22


void main()
{
clrscr();
Piggy Tom(100);
Piggy Sue(200);
Tom.ShowSavings("Tom");
Sue.ShowSavings("Sue");
Tom += 300;
Sue -= 100;
// Tom = Tom + 300;
// Sue = Sue - 100;
Tom.ShowSavings("Tom");
Sue.ShowSavings("Sue");
getch();
}

Piggy::Piggy(int Temp)
{
Savings = Temp;
}

void Piggy::ShowSavings(apstring Name) const


{
cout << endl;
cout << Name << " has " << Savings
<< " dollars saved" << endl;
}

PROG2812.CPP OUTPUT

Tom has 100 dollars saved

Sue has 200 dollars saved

Tom has 400 dollars saved

Sue has 100 dollars saved

Program PROG2813.CPP reminds you that overloaded operator functions are


genuine member functions of a class that can be called with the dot-function

Chapter XXVIII Overloaded Operators 28.23


notation. Using this notation may also help in understanding the required
parameter passing better.

// PROG2813.CPP
// This program shows how to call the overloaded += and -=
// operators using the dot-function notation.

#include <iostream.h>
#include <conio.h>
#include "APSTRING.H"

class Piggy
{
private:
float Savings;
public:
Piggy(int);
void ShowSavings(apstring Name) const;
void operator+= (float Money) { Savings += Money; }
void operator-= (float Money) { Savings -= Money; }
};

void main()
{
clrscr();
Piggy Tom(100);
Piggy Sue(200);
Tom.ShowSavings("Tom");
Sue.ShowSavings("Sue");
Tom.operator+=(300);
Sue.operator-=(100);
Tom.ShowSavings("Tom");
Sue.ShowSavings("Sue");
getch();
}

Piggy::Piggy(int Temp)
{
Savings = Temp;
}

void Piggy::ShowSavings(apstring Name) const


{
cout << endl;
cout << Name << " has " << Savings
<< " dollars saved" << endl;
}

Chapter XXVIII Overloaded Operators 28.24


PROG2813.CPP OUTPUT

Tom has 100 dollars saved

Sue has 200 dollars saved

Tom has 400 dollars saved

Sue has 100 dollars saved

Program examples like PROG2812.CPP and PROG2813.CPP can be easily


altered with small changes to use other operators, like *= and /=, which use the
exact same logic. This brings up an interesting question. Can you create an
overloaded operator with some totally new and different functionality?
Everything that you have seen does exist in C++ right now. With K defined as an
integer, statements like . . . K++, ++K, K--, --K, K+=5, K/=10 all make sense.
And it has been such statements that we have overloaded and given similar
meanings in the Piggy class.

So are there operator limitations? There are not many operators that cannot be
overloaded. Several operators have a special significance in C++ and any attempt
to give secondary, overloaded meaning to these operators will get you some
serious compiling errors. A small chart will follow with the few C++ operators
that cannot be overloaded, followed by a larger chart of all the many C++
operators that allow overloading. Do not be surprised if you see foreign looking
operators. Some of the strange operators will be make sense in some future
chapter.

Operators That Cannot Be Overloaded

The following C++ operators cannot be overloaded:


. dot operator
:: scope resolution operator

Chapter XXVIII Overloaded Operators 28.25


?: conditional operator
.* pointer-to-member operator

Operators That Can Be Overloaded

The following C++ operators can be overloaded:

+ - * / % ^ & | ~ ! =
+= -= *= /= %= ^= &= |=
< > <= >= << >> <<= >>= == !=
++ -- && || -> ->* , [] ()

28.6 Creating a Power Operator

In this section we will see that it is possible to implement operator^ as a “power”


function. This type of functionality exists in C++ but it does not exist in C++
with an operator. You can make a program statement like Result = pow(5,3) ,
and that statement will assign 5 to the 3rd power (125) to Result. We are
interested in using some operator to achieve this same result. Such operators exist
in various other languages and we want to add this overloaded capability to the
C++ programming language as well, at least with our own classes.
Now for this entire chapter, Piggy has done such a nice job for us. It is more
than a little peculiar to use the Piggy class to raise some attribute value to a
power, but that is all right. You have security with this piggybank class and just
because it is weird does not mean that you cannot learn from the example.

By now you will see that the pattern for creating an operator function is quite
consistent. In the case of operator^(int) the intention is to take the Savings
attribute and raise it to the power of the int parameter. This type function is

Chapter XXVIII Overloaded Operators 28.26


shown in program PROG2814.CPP and do not ask why anybody would want a
program that raises savings to a power. Maybe it represents a new interest
approach or perhaps it is the formula Bill Gates uses for his piggybank.

// PROG2814.CPP
// This program demonstrates how to implement a function that does
// not exist in C++. In this example ^ is used to create
// a power operator.

#include <iostream.h>
#include <conio.h>
#include <math.h>
#include "APSTRING.H"

class Piggy
{
private:
float Savings;
public:
Piggy(int);
void ShowSavings(apstring Name) const;
void operator^ (float Power)
{ Savings = pow(Savings,Power); }
};

void main()
{
clrscr();
Piggy Tom(2);
Piggy Sue(5);
Tom.ShowSavings("Tom");
Sue.ShowSavings("Sue");
Tom^5;
Sue^3;
Tom.ShowSavings("Tom");
Sue.ShowSavings("Sue");
getch();
}

Piggy::Piggy(int Temp)
{
Savings = Temp;
}

void Piggy::ShowSavings(apstring Name) const

Chapter XXVIII Overloaded Operators 28.27


{
cout << endl;
cout << Name << " has " << Savings
<< " dollars saved" << endl;
}

PROG2814.CPP OUTPUT

Tom has 2 dollars saved

Sue has 5 dollars saved

Tom has 32 dollars saved

Sue has 125 dollars saved

Many pages can be filled up with examples that show all types of exotic
functions. Examples are good but at this stage you should be able to use your
imagination and try out some functions on your own. This chapter will by no
means give you the complete array of operator possibilities. That could fill a
book, but it my intention to show you a large variety of operator functions so that
you can use them comfortably in any of your own classes.

28.7 Return Operator Functions

Every overloaded operator function you have seen has been a void function, and
these functions have done just fine for the examples presented. There are
situations where a void function cannot handle the required operation. You will
need some type of return operator function.

Put on your seatbelt because we are about to press down on the gas and make this
overloaded journey just a bit more interesting. Do not be afraid to reread some of
these sections, because it is unlikely that everything will soak in the first time.

Void functions have worked nicely for situations like K++, --K, K+=10, and
other similar operations. What about a statement like K = L++ ? Can that work

Chapter XXVIII Overloaded Operators 28.28


with the functions shown so far? Some alert students are now mentioning that
this very book in the past made reference to combining shortcuts, and what should
not be done. Looks like an example here of the very type that you were told not
to use. That is true, but you did learn about the possibility of certain program
statements. Frequently, you are shown program features that C++ allows, and
that you may encounter in actual programs. At the same time you are told that it
is better to avoid such features. So work with me and it will be easier to help
explain the type of operator function presented in this section.

The statement K = L++ works fine with K and L both integers. The statement
will not compile if K and L are objects of the Piggy class. The compiler will
respond with a message like not an allowed type. Such an error message should
not be a total surprise. You start with a void function like operator++ and use it
to alter Savings. But the function is void, and cannot be used as a value in an
assignment program statement. Program PROG2815.CPP should convince you
that it is not possible to use overloaded void functions in an assignment.

// PROG2815.CPP
// This program attempts to use the operator++ function in an
// assignment statement. The program does not compile because
// a return function is required for the assignment.

#include <iostream.h>
#include <conio.h>
#include "APSTRING.H"

class Piggy
{
private:
float Savings;
public:
Piggy();
Piggy(int);
void ShowSavings(apstring Name) const;
void operator++();
};

void main()
{
clrscr();
Piggy Tom(200);
Piggy Sue(1400);
Tom.ShowSavings("Tom");
Sue.ShowSavings("Sue");
Tom = ++Sue;
Tom.ShowSavings("Tom");
getch();
}

Chapter XXVIII Overloaded Operators 28.29


Piggy::Piggy()
{
Savings = 0;
}

Piggy::Piggy(int Temp)
{
Savings = Temp;
}

void Piggy::ShowSavings(apstring Name) const


{
cout << endl;
cout << Name << " has " << Savings
<< " dollars saved" << endl;
}

void Piggy::operator++()
{
cout << endl;
cout << "OPERATOR FUNCTION" << endl;
Savings += 10;
Piggy Temp;
Temp.Savings = Savings;
}

PROG2815.CPP OUTPUT

Compiling PROG2815.CPP:
Error PROG2815.CPP 31: Not an allowed type

The compiler stops at the statement Tom = ++Sue; and complain that this is Not
an allowed type. So we need a new approach to handle this problem. If you are
going to assign one object to another object than a return function is needed. In
our earlier example the operator++ function needs to return a Piggy type.

Returning a type sounds easy, but watch out that you do not mistakenly return the
data type rather than a variable of a type. You may declare a function with the
heading int Hello(float) but later in the function body you will not use return int.
Your function will use a statement like return 5 or return X, or any other
statement that returns an integer value.

So how do we return a value of type Piggy? You cannot say return Piggy. That
was just established as a problem. Take a look at program example

Chapter XXVIII Overloaded Operators 28.30


PROG2816.CPP which presents the familiar operator++ but this time the
function is a return function with type Piggy. See how it is done.

// PROG2816.CPP
// This program demonstrates how to use an operator function that
// returns an object. In this example a temporary object is used
// to return an object.

#include <iostream.h>
#include <conio.h>
#include "APSTRING.H"

class Piggy
{
private:
float Savings;
public:
Piggy();
Piggy(int);
void ShowSavings(apstring Name) const;
Piggy operator++();
};

void main()
{
clrscr();
Piggy Tom(200);
Piggy Sue(1400);
Tom.ShowSavings("Tom");
Sue.ShowSavings("Sue");
Tom = ++Sue;
Tom.ShowSavings("Tom");
Sue.ShowSavings("Sue");
getch();
}

Piggy::Piggy()
{
Savings = 0;
}

Piggy::Piggy(int Temp)
{
Savings = Temp;
}

void Piggy::ShowSavings(apstring Name) const


{
cout << endl;
cout << Name << " has " << Savings
<< " dollars saved" << endl;
}

Piggy Piggy::operator++()
{
cout << endl;

Chapter XXVIII Overloaded Operators 28.31


cout << "OPERATOR FUNCTION" << endl;
Savings += 10;
Piggy Temp;
Temp.Savings = Savings;
return Temp;
}

PROG2816.CPP OUTPUT

Tom has 200 dollars saved

Sue has 1400 dollars saved

OPERATOR FUNCTION

Tom has 1410 dollars saved

The secret of the operator++ function is the locally declared Temp of type
Piggy. The function performs the same type of incrementing with the Savings
attribute as the previous operator++ function. After Savings is incremented a
new Piggy object is constructed, appropriately called Temp. The only
information of concern is Savings and that value is assigned to the Savings field
of the Temp object. The function finishes with return Temp and we are happy .

Return Function Warning

Return functions require a return statement that uses


the same data type as the return function heading indicates.

You return a value of some data type, not the data type itself.

Correct: return 15;


Incorrect: return int;

It is also possible to create a return function without defining a local object. You
can use the trusty this pointer to help out. Have you forgotten about the this
pointer? You know that strange pointer explained in the encapsulation chapter.

Chapter XXVIII Overloaded Operators 28.32


The purpose of the this pointer is to help identify the current object in use, even
though an identifier is not used.

Perhaps a very brief refresher will clear this (pun intended) up. The next program
displays the memory locations of an object defined in the main function as well as
the memory location of the this pointer in several member functions. The
program example demonstrates that the memory locations of the new object and
the invisible this pointer are identical. In other words, the this pointer keep track
of the current object in an invisible, background sort-of-way.
// PROG2817.CPP
// This program reviews the "this" pointer.

#include <iostream.h>
#include <conio.h>
#include "APSTRING.H"

class Piggy
{
public:
Piggy();
void Action1();
void Action2();
};

void main()
{
clrscr();
Piggy Tom;
cout << endl;
cout << "MAIN FUNCTION" << endl;
cout << "Address of Tom " << &Tom << endl;
getch();
Tom.Action1();
Tom.Action2();
getch();
}

Piggy::Piggy()
{
cout << endl;
cout << "CONSTRUCTOR CALL" << endl;
cout << "Address of this " << this << endl;
}

void Piggy::Action1()
{
cout << endl;
cout << "ACTION1 CALL" << endl;
cout << "Address of this " << this << endl;
}

Chapter XXVIII Overloaded Operators 28.33


void Piggy::Action2()
{
cout << endl;
cout << "ACTION2 CALL" << endl;
cout << "Address of this " << this << endl;
}

PROG2817.CPP OUTPUT

CONSTRUCTOR CALL
Address of this 0x8f5bfff4

MAIN FUNCTION
Address of Tom 0x8f5bfff4

ACTION1 CALL
Address of this 0x8f5bfff4

ACTION2 CALL
Address of this 0x8f5bfff4

Great, the this business has been recalled or refreshed, and if you are still
confused you need to go back several chapters and review that material. There
was more detail about this this pointer in the encapsulation chapter. Now
assuming that there is a good understanding of the this pointer, the issue of
returning an object becomes possible without any need for a temporary object
definition. Be aware that the program example below is not complete.

// PROG2818.CPP
// This program demonstrates how to use an operator function that
// returns an object. In this example the "this" pointer is used
// to return an object.

#include <iostream.h>
#include <conio.h>
#include "APSTRING.H"

class Piggy
{

Chapter XXVIII Overloaded Operators 28.34


private:
float Savings;
public:
Piggy();
Piggy(int);
void ShowSavings(apstring Name) const;
Piggy operator++();
};

Piggy Piggy::operator++()
{
cout << endl;
cout << "OPERATOR FUNCTION" << endl;
Savings += 10;
return *this;
}

Program PROG2818.CPP was not shown in its entirety, nor is output shown.
The output and program is identical to the previous program. The only difference
is the operator++ function, which uses this. The most common problem that
happens is using return this rather than return *this.

Returning Object Values

Operator functions can return object values two ways:

Define a local object, assign appropriate values to the local


object and then use its identifier in the return statement.

Piggy Piggy::operator++()
{
cout << endl;
cout << "OPERATOR FUNCTION" << endl;
Savings += 10;
Piggy Temp;
Temp.Savings = Savings;
return Temp;
}

It is also possible to use the this pointer and return the


current object with return*this. Make sure to include
the dereference asterisk so that the object is returned and

Chapter XXVIII Overloaded Operators 28.35


not the memory address of the object.

Piggy Piggy::operator++()
{
cout << endl;
cout << "OPERATOR FUNCTION" << endl;
Savings += 10;
return *this;
}

28.8 Binary Operators

How about a total switch to something different like binary operators. In the
beginning of this chapter I showed how neatly you could do stuff, such as matrix
multiplication with a statement like M3 = M1 * M2; assuming that you have
three objects here. This multiplication statement is very common in mathematics,
but it is not anything you have seen with overloaded operators so far.

Overloaded binary operators behave very differently and require a syntax that
introduces a variety of new twists to the overloaded operator story. What type of
stuff would you expect? For starters you will need to work with return functions.
Does that make sense? Think of a binary operator statement, such as the one
above. The result of the binary operation is assigned to an object. The last
section showed that such an assignment is only possible if you use a return
function. This part is not so tough and you know two ways to achieve a return
function: using a temporary object, and using the this pointer.

Consider the following operator function for the binary plus operator. Does the
function not seem quite logical. Parameter LHS means Left Hand Side and
parameter RHS means Right Hand Side. These parameters are compared to the
plus operator. In other words, in the program statement X = Y + Z, variable X
gets the return value of the function, the operand Y is LHS and Z is RHS.

BINARY OPERATOR+ ASSUMED CORRECT FUNCTION EXAMPLE

Piggy Piggy::operator+(Piggy LHS, Piggy RHS)

Chapter XXVIII Overloaded Operators 28.36


{
Piggy Temp;
Temp.Savings = LHS.Savings + RHS.Savings;
return Temp;
}

The example above would work just lovely if C++ used the same meaning for
LHS and RHS as the function above. Too bad, C++ has different ideas, and the
first big surprise is that the binary operator+ function uses only one parameter.
Somehow LHS is swinging in the breeze somewhere and totally forgotten.

Now remember that a member function always contains a this pointer and the
this pointer identifies some object that is currently in scope. C++ has decided that
the Left Hand Side (LHS) of a binary operation will be represented by the this
pointer. Furthermore, you know that this does not require parameter passing.
The clever this pointer just shows up, invited or not. Now considering the special
job of the this pointer the correct function below is not all that different from the
assumed function above. Any concerns about the const ampersand reference
parameter stuff? Hopefully not. At several occasions a point was made that data
structures should be passed by reference for efficiency sake. At the same time
you were told that it is wise to use const parameters if data values do not change.

BINARY OPERATOR+ CORRECT EXAMPLE

Piggy Piggy::operator+(const Piggy &RHS)


{
cout << endl;
cout << "BINARY + OPERATOR FUNCTION" << endl;
Piggy Temp;
Temp.Savings = (*this).Savings + RHS.Savings;
return Temp;
}

With some new information under your belt you are now ready to digest program
PROG2819.CPP that shows how to use the operator+ binary function. In
particular, notice how the main function shows a program statement with a binary
operation that is identical in appearance to any mathematical operation.

Chapter XXVIII Overloaded Operators 28.37


// PROG2819.CPP
// This program demonstrates how to implement a binary + operator
// function.

#include <iostream.h>
#include <conio.h>
#include "APSTRING.H"

class Piggy
{
private:
float Savings;
public:
Piggy();
Piggy(int);
void ShowSavings(apstring Name) const;
Piggy operator+(const Piggy & RHS);
};

void main()
{
clrscr();
Piggy Tom(200);
Piggy Sue(1400);
Piggy Larry(0);
Tom.ShowSavings("Tom");
Sue.ShowSavings("Sue");
Larry.ShowSavings("Larry");
Larry = Tom + Sue;
Tom.ShowSavings("Tom");
Sue.ShowSavings("Sue");
Larry.ShowSavings("Larry");
getch();
}

Piggy::Piggy()
{
Savings = 0;
}

Piggy::Piggy(int Temp)
{
Savings = Temp;
}

void Piggy::ShowSavings(apstring Name) const


{
cout << endl;

Chapter XXVIII Overloaded Operators 28.38


cout << Name << " has " << Savings
<< " dollars saved" << endl;
}

Piggy Piggy::operator+(const Piggy &RHS)


{
cout << endl;
cout << "BINARY + OPERATOR FUNCTION" << endl;
Piggy Temp;
Temp.Savings = (*this).Savings + RHS.Savings;
return Temp;
}

PROG2819.CPP OUTPUT

Tom has 200 dollars saved

Sue has 1400 dollars saved

Larry has 0 dollars saved

BINARY + OPERATOR FUNCTION

Tom has 200 dollars saved

Sue has 1400 dollars saved

Larry has 1600 dollars saved

The program output displays the desired result. The plus operator is meant to
achieve the addition of Tom and Sue such that Larry gets the sum of their
savings. Larry is happy, but are you perhaps surprised that my program example
returned to using the local, temporary object in the operator+ function? Did we
not establish that this can handle the situation nicely with less program

Chapter XXVIII Overloaded Operators 28.39


statements? You are thinking correctly, but hang on and check out what happens
when you use operator+ with dot.function notation.

// PROG2820.CPP
// This program demonstrates how to implement a binary + operator
// using dot-function notation.

#include <iostream.h>
#include <conio.h>
#include "APSTRING.H"

class Piggy
{
private:
float Savings;
public:
Piggy();
Piggy(int);
void ShowSavings(apstring Name) const;
Piggy operator+(const Piggy &RHS);
};

void main()
{
clrscr();
Piggy Tom(200);
Piggy Sue(1400);
Piggy Larry(0);
Tom.ShowSavings("Tom");
Sue.ShowSavings("Sue");
Larry.ShowSavings("Larry");
Larry = Tom.operator+(Sue); // Larry = Tom + Sue
Tom.ShowSavings("Tom");
Sue.ShowSavings("Sue");
Larry.ShowSavings("Larry");
getch();
}

Piggy::Piggy()
{
Savings = 0;
}

Piggy::Piggy(int Temp)
{

Chapter XXVIII Overloaded Operators 28.40


Savings = Temp;
}

void Piggy::ShowSavings(apstring Name) const


{
cout << endl;
cout << Name << " has " << Savings
<< " dollars saved" << endl;
}

Piggy Piggy::operator+(const Piggy &RHS)


{
cout << endl;
cout << "BINARY + OPERATOR FUNCTION" << endl;
Piggy Temp;
Temp.Savings = (*this).Savings + RHS.Savings;
return Temp;
}

PROG2820.CPP OUTPUT

Tom has 200 dollars saved

Sue has 1400 dollars saved

Larry has 0 dollars saved

BINARY + OPERATOR FUNCTION

Tom has 200 dollars saved

Sue has 1400 dollars saved

Larry has 1600 dollars saved

Anytime that dot.function notation is used, you need to use some object variable
followed by a dot and then the operator function being used. This may help to
explain why there is only one parameter passed with a binary operator. Since the
this pointer take care of the LHS, it is quite easy to sneak in a side effect.

Program PROG2821.CPP is very similar to the previous program, except for the
different implementation of the overloaded operator+ function. Your good
friend the this pointer is in charge. Only the different operator+ function is
shown along with the sample output execution. Take a close look at the execution
and see if anything appears odd at all.

// PROG2821.CPP
// This program uses *this to return information with the

Chapter XXVIII Overloaded Operators 28.41


// binary + operator function. This approach has a bad
// side effect.

Piggy Piggy::operator+(const Piggy &RHS)


{
cout << endl;
cout << "BINARY + OPERATOR FUNCTION" << endl;
Savings += RHS.Savings;
return *this;
}

PROG2821.CPP OUTPUT

Tom has 200 dollars saved

Sue has 1400 dollars saved

Larry has 0 dollars saved

BINARY + OPERATOR FUNCTION

Larry has 1600 dollars saved

Tom has 1600 dollars saved

Sue has 1400 dollars saved

Apparently Larry is not the only one who is happy. Somehow Tom has benefited
rather nicely from this addition process. Returning *this may seem like a good
idea, but it requires that you alter (*this).Savings to return the proper results to
Larry. But there is a bad side effect. The this pointer keeps track of the LHS of
the binary operation, which in this case happens to be Tom. You see the
interesting situation that we achieve the correct mathematical result, but the same
result shows up in an unwanted place. This proves that sometimes constructing a
temporary object is required.
Binary Operator Notes

Consider the program statement Sum = Object1 + Object2;


Object1 is usually called LHS (Left Hand Side).
Object2 is usually called RHS (Right Hand Side).

The LHS of the binary operation is referenced by the this


pointer and does not need to be passed as a parameter.

Chapter XXVIII Overloaded Operators 28.42


The RHS of the binary operation does need to be passed
as a parameter. Remember that such a parameter should
be a const reference parameter.

It is important to define a local object for the return function.


Using the this pointer in the operator+ function will not only
change desired values in the target object, but also in an
object that should not be changed.

Correct Example:

Piggy Piggy::operator+(const Piggy &RHS)


{
Piggy Temp;
Temp.Savings = (*this).Savings + RHS.Savings;
return Temp;
}

Incorrect Example:

Piggy Piggy::operator+(const Piggy &RHS)


{
Savings += RHS.Savings;
return *this;
}

28.9 Comparison Operators

It is not always necessary for a return operator function to return some object
value. This was shown earlier for a good reason because you must return an
object value for any overloaded binary operators. There are other operations that
require a return function, but a Boolean value needs to be returned, and these
functions are the comparison operators.

Included in this group are equal, not equal, greater than, less than, greater than or
equal, and less than or equal. The assumption is that you will use the proper

Chapter XXVIII Overloaded Operators 28.43


relational operators for overloading comparison operators. Remember, that it is
easy to make an overloaded operator behave in a very counter-intuitive manner.

By now you have seen the pattern of showing one example with the
understanding that the same logic applies to other operators of the same category.
Program PROG2822.CPP shows how to declare an equality comparison with the
operator== function. Equality in this case means that two Piggy objects each
have the same amount saved.

// PROG2822.CPP
// This program demonstrates how to implement a comparison
// function. This example uses the operator== function to
// check if the amount of savings is equal.

#include <iostream.h>
#include <conio.h>
#include "APSTRING.H"

class Piggy
{

private:
float Savings;

public:
Piggy(int);
void operator+=(int);
bool operator==(const Piggy & RHS) const;
};

void main()
{
clrscr();
Piggy Tom(200);
Piggy Sue(200);

if (Tom == Sue)
cout << "Tom's savings and Sue's savings are equal" << endl;
else
cout << "Tom's savings and Sue's savings are not equal"
<< endl;
Tom += 1000;
Sue += 500;
if (Tom == Sue)

Chapter XXVIII Overloaded Operators 28.44


cout << "Tom's savings and Sue's savings are equal" << endl;
else
cout << "Tom's savings and Sue's savings are not equal"
<< endl;
getch();
}

Piggy::Piggy(int Temp)
{
Savings = Temp;
}

void Piggy::operator+=(int Temp)


{
Savings += Temp;
}

bool Piggy::operator==(const Piggy &RHS) const


{
return Savings == RHS.Savings;
}

PROG2822.CPP OUTPUT

Tom's savings and Sue's savings are equal


Tom's savings and Sue's savings are not equal

Note that the LHS and RHS type of logic is also showing up with the
operator== function. Once again, the LHS is carried by the this pointer and the
RHS of the equality comparison is passed as a parameter.

Program PROG2823.CPP demonstrates a slight variation on the comparison


theme. The previous program example compared the savings of two objects and
returned true or false. In this example the savings of the current object are
compared to some constant value.
// PROG2823.CPP
// This program demonstrates how an object can be compared to
// some numerical value.

#include <iostream.h>
#include <conio.h>
#include "APSTRING.H"

class Piggy

Chapter XXVIII Overloaded Operators 28.45


{
private:
float Savings;
public:
Piggy(int);
void operator+=(int);
bool operator==(float RHS) const;
};

void main()
{
clrscr();
Piggy Tom(200);
if (Tom == 200)
cout << "Tom has saved $200.00" << endl;
else
cout << "Tom does not have $200.00 saved" << endl;
Tom += 100;
if (Tom == 200)
cout << "Tom has saved $200.00" << endl;
else
cout << "Tom does not have $200.00 saved" << endl;
getch();
}

Piggy::Piggy(int Temp)
{
Savings = Temp;
}

void Piggy::operator+=(int Temp)


{
Savings += Temp;
}

bool Piggy::operator==(float RHS) const


{
return Savings == RHS;
}

PROG2823.CPP OUTPUT

Tom has saved $200.00


Tom does not have $200.00 saved

Chapter XXVIII Overloaded Operators 28.46


Comparison Operator Notes

A comparison operator function returns a bool value.

Comparison functions use the this pointer to represent the


LHS of the comparison operator and use a parameter to
pass the RHS of the comparison operator.

Example:

bool Piggy::operator==(const Piggy &RHS)


{
return Savings == RHS.Savings;
}

28.10 Assignment Operators

This section will look suspiciously like the a section straight from the copy
constructor section of the last chapter. C++ has a default assignment operator
that works with classes. This means that it is possible to define two objects of the
same class and at some later point assign one object to another object with the
assignment ( = ) operator.

The problem is that such an assignment makes a member-for-member copy of


the object. You see where this is heading because we had the same problem as
the default copy constructor that also makes a pretty good copy, called a shallow
copy, but not a deep copy. Deep copies also handle potential memory allocation
problems that arise when pointers are in the picture. Program PROG2824.CPP
uses the same pointer logic of the last chapter to show possible pitfalls that can
happen when you use the C++ default assignment operator.

Chapter XXVIII Overloaded Operators 28.47


// PROG2824.CPP
// This program demonstrates that problems can arise when the
// C++ default assignment operation is used. Changes in the
// second object also bring about changes in the first object.

#include <iostream.h>
#include <conio.h>

class Pointers
{
public:
int *IntPtr;
public:
Pointers();
~Pointers();
void ShowData(int N) const;
};

void CreateP2(const Pointers &Obj);

void main()
{
clrscr();
Pointers P1;
cout << "IN MAIN FUNCTION" << endl;
P1.ShowData(1);
CreateP2(P1);
cout << "IN MAIN FUNCTION" << endl;
P1.ShowData(1);
}

Pointers::Pointers()
{
IntPtr = new int;
*IntPtr = 12345;
}

Pointers::~Pointers()
{
cout << endl;
delete(IntPtr);
}

void Pointers::ShowData(int N) const


{
cout << endl;
cout << "Object P" << N << endl;
cout << "IntPtr " << IntPtr << endl;
cout << "*IntPtr " << *IntPtr << endl;

Chapter XXVIII Overloaded Operators 28.48


getch();
}

void CreateP2(const Pointers &Obj)


{
Pointers P2;
P2 = Obj;
cout << endl << "IN CREATE2 FUNCTION" << endl;
P2.ShowData(2);
*P2.IntPtr = 1000;
P2.ShowData(2);
}

PROG2824.CPP OUTPUT

Object P1
IntPtr 0x8f8bde0
*IntPtr 12345

Object P2
IntPtr 0x8f8bde0
*IntPtr 12345

Object P2
IntPtr 0x8f8bde0
*IntPtr 1000

OBJECT IS DESTROYED

Object P1
IntPtr 0x8f8bde0
*IntPtr 3548

OBJECT IS DESTROYED

The sample output shows the problem. Object P2 becomes a copy of P1 and then
some change occurs in P2 and P2 leaves scope before P1 is finished. Suddenly
P1 finds itself swinging in the breeze and staring at some weird value.

The problem is solved in the same manner as it was done with the default copy
constructor. Do not rely on the default stuff, create your own. In this case you
must create your own overloaded operator= function that will make a proper
deep copy of the source object. In this manner any change in the source object
will not alter values in the target object. The user-defined operator= is shown in
the next program example.

Chapter XXVIII Overloaded Operators 28.49


// PROG2825.CPP
// This program solves the problem of the default assignment
// operator by using an operator= function that copies everything,
// and sets aside memory for the copied object.

#include <iostream.h>
#include <conio.h>

class Pointers
{
public:
int *IntPtr;
public:
Pointers(); // constructor
~Pointers(); // destructor
void ShowData(int) const;
Pointers operator=(const Pointers &Obj);
};

void CreateP2(const Pointers &Obj);

void main()
{
clrscr();
Pointers P1;
cout << "IN MAIN FUNCTION" << endl;
P1.ShowData(1);
CreateP2(P1);
cout << "IN MAIN FUNCTION" << endl;
P1.ShowData(1);
}

Pointers::Pointers()
{
IntPtr = new int;
*IntPtr = 12345;
}

Pointers::~Pointers()
{
cout << endl;
delete(IntPtr);
}

void Pointers::ShowData(int N) const


{
cout << endl;
cout << "Object P" << N << endl;

Chapter XXVIII Overloaded Operators 28.50


cout << "IntPtr " << IntPtr << endl;
cout << "*IntPtr " << *IntPtr << endl;
getch();
}

void CreateP2(const Pointers &Obj)


{
Pointers P2;
P2 = Obj;
cout << "IN CREATEP2 FUNCTION" << endl;
P2.ShowData(2);
*P2.IntPtr = 1000;
P2.ShowData(2);
}

Pointers Pointers::operator=(const Pointers &RHS)


{
IntPtr = new int;
*IntPtr = *(RHS.IntPtr);
return *this;
}

PROG2825.CPP OUTPUT

Object P1
IntPtr 0x8f8bde0
*IntPtr 12345

Object P2
IntPtr 0x8f8bde0
*IntPtr 12345

Object P2
IntPtr 0x8f8bde0
*IntPtr 1000

OBJECT IS DESTROYED

Object P1
IntPtr 0x8f8bde0
*IntPtr 12345

OBJECT IS DESTROYED

Chapter XXVIII Overloaded Operators 28.51


User-Defined Assignment Operators Are Important

C++ provides a default assignment operator for objects, which


makes a member-for-member copy. Declare your own
overloaded assignment operator to make a deep copy that
protects against potential memory allocation problems.

28.11 Free Operator Functions

It is quite possible that you believe it to be a requirement that an overloaded


operator function is a member function of a class. That has certainly been true for
all the program examples that have been shown so far. It turns out that it is
possible for an overloaded operator function to be a free function, which is a
function that is not a member of any class. This is possible as long as the free
function is associated to some class with object parameter passing.

Free Function

A free function is a function that is declared outside any


class. It is not a member function of any class.

class Example
{
private:
int X;
public:
void SetX(int N);
int GetX() const;
};

void Hello(const Example &X)


{
}

In this example SetX and GetX are member functions


of Example, while Hello is a free function.

Chapter XXVIII Overloaded Operators 28.52


This begs the question, just "why might we need free functions?" A few sections
ago you were learning about binary operator functions. You recall that we had
various problems with binary operator functions. The use of the this pointer did
not work because it created a bad side effect. It appeared that the LHS, which is
referenced by this can be altered unintentionally. What is bothersome about this
scenario is that object oriented programming makes such a big deal about
reliability. Consider one of the earlier binary operator functions.

Piggy Piggy::operator+(const Piggy &RHS)


{
cout << endl;
cout << "BINARY + OPERATOR FUNCTION" << endl;
Piggy Temp;
Temp.Savings = Savings + RHS.Savings;
return Temp;
}

Take a look at RHS and look at the caution employed. The object is passed by
reference to avoid inefficient copying of data and inefficient memory usage. At
the same time const does an excellent job insuring that RHS only pays a visit,
snoops around, but does not get modified. You have seen similar precautions
used with accessor member functions that use a const on the right side of the
function heading.

So what has been done for the left hand side? Basically, nothing and there is no
protection in sight. Now if we could create a function, such as the one below than
the exact same protection could be afforded to LHS.

Piggy Piggy::operator+(const Piggy &LHS, const Piggy


&RHS)
{
cout << endl;
cout << "BINARY + OPERATOR FUNCTION" << endl;
Piggy Temp;
Temp.Savings = LHS.Savings + RHS.Savings;
return Temp;
}

Chapter XXVIII Overloaded Operators 28.53


The two-parameter function seems ideal for the job. We now have protection for
both the left side and the right side. Too bad that this function will not compile. I
do hope you realize that LHS must be associated with an object, and that requires
that the function uses a single operator.

Still the idea has merit and let us explore the possibility of using an overloaded
operator function that is not a member function. This so called free function is
also free of the requirement of a single parameter. Maybe that could be a solution
to the problem that we are trying to solve.

Program example PROG2826.CPP bravely plunges in and boldy declares the


operator+ function outside the Piggy class. This makes it a free function and you
will notice that two parameter are used. Will this work?

// PROG2826.CPP
// This program attempts to create an overloaded free function.
// The program does not compile because Savings is not accessible.

#include <iostream.h>
#include <conio.h>
#include "APSTRING.H"

class Piggy
{
public:
Piggy(int N)
: Savings(N) { }
void ShowSavings(apstring Name) const;
private:
float Savings;
};

Piggy operator+(const Piggy &LHS, const Piggy &RHS);

void main()
{
clrscr();
Piggy Tom(200);
Piggy Sue(1400);
Piggy Larry(0);
Tom.ShowSavings("Tom");
Sue.ShowSavings("Sue");
Larry.ShowSavings("Larry");
Larry = Tom + Sue;
Tom.ShowSavings("Tom");
Sue.ShowSavings("Sue");
Larry.ShowSavings("Larry");
getch();
}

Chapter XXVIII Overloaded Operators 28.54


void Piggy::ShowSavings(apstring Name) const
{
cout << endl;
cout << Name << " has " << Savings << " dollars saved" << endl;
}

Piggy operator+(const Piggy &LHS, const Piggy &RHS)


{
Piggy Temp(0);
Temp.Savings = LHS.Savings + RHS.Savings;
return Temp;
}

PROG2826.CPP OUTPUT

Compiling PROG2826.CPP:
Error PROG2826.CPP 51: 'Piggy::Savings' is not
accessible

That was a lovely idea, but our clever program does not even compile. Still, that
is not such a great shock because we are rather casually trying to access Savings,
and that happens to be private data. It is fine to depart from the class boundaries,
but do not expect to access any private class members.

Program PROG2827.CPP presents an interesting solution to the problem. C++


has a special feature, called a friend function. A friend function looks like a
member function, sort of. Check out the next program example first and see what
is happening. Is operator+ a member function or not.

// PROG2827.CPP
// This program solves the Savings access problem by making the
// binary operator+ function a friend function. This approach
// is not recommended by the AP committee which strongly
// discourages the use of a friend function because using friend
// functions violates encapsulation.

#include <iostream.h>
#include <conio.h>
#include "APSTRING.H"

class Piggy
{
public:
Piggy(int N)
: Savings(N) { }

Chapter XXVIII Overloaded Operators 28.55


void ShowSavings(apstring Name) const;
friend Piggy operator+(const Piggy &LHS, const Piggy &RHS);
private:
float Savings;
};

void main()
{
clrscr();
Piggy Tom(200);
Piggy Sue(1400);
Piggy Larry(0);
Tom.ShowSavings("Tom");
Sue.ShowSavings("Sue");
Larry.ShowSavings("Larry");
Larry = Tom + Sue;
Tom.ShowSavings("Tom");
Sue.ShowSavings("Sue");
Larry.ShowSavings("Larry");
getch();
}

void Piggy::ShowSavings(apstring Name) const


{
cout << endl;
cout << Name << " has " << Savings << " dollars saved" << endl;
}

Piggy operator+(const Piggy &LHS, const Piggy &RHS)


{
Piggy Temp(0);
Temp.Savings = LHS.Savings + RHS.Savings;
return Temp;
}

PROG2827.CPP OUTPUT

Tom has 200 dollars saved

Sue has 1400 dollars saved

Larry has 0 dollars saved

Tom has 200 dollars saved

Sue has 1400 dollars saved

Chapter XXVIII Overloaded Operators 28.56


Larry has 1600 dollars saved

There is program output, and the output is correct. So what is happening with the
magic friend word. If you look inside the Piggy class you see that operator+ is
present there. At least a prototypes is present there preceded by the odd-looking
word, friend. If you are looking at this on the computer, you will notice that
friend is bold and appears to be a reserved word in C++.

The problem comes later at the implementation of operator+ since there is no


scope resolution operator used, you should conclude that this is not the
implementation of a member function.

You are quite correct. You are looking at a free function that has special
privileges. The operator+ is a free function, but it is also a friend function of the
Piggy class. This was accomplished by placing the prototype of operator+ inside
the Piggy class preceded by the friend keyword. C++ is happy now and allows a
special exception, since you are a friend I will let you have access to the private
data of this class.

Friend Function

A friend function is a free function that has access to the


private segment of a designated class.

The class is designated by placing the prototype of the


free function inside the class declaration, preceded by
the reserved word friend.

The use of friend functions is discouraged by the College


Board APCS committee and the APCS Examination does
not include any questions on the friend function topic.

This friend business is all quite wonderful but you will be pleased to know that
the College Board APCS Committee is not excited about the use of friend
functions. The friend concept will not be tested and the use of friends is

Chapter XXVIII Overloaded Operators 28.57


discouraged. So it appears that we have a solution, but we are not out of the
woods and on the way home yet.

Well let us not give up and see if there is another way to approach this problem.
Perhaps we can accomplish the same type of idea without the use of the forbidden
friend functions. After all, it is very legitimate to have public member functions
that access private data, and such functions can indirectly access the necessary
data for your free function. Such a solution is provided in PROG2828.CPP, and
this approach avoids the use of friend functions.

In preparation for the APCS Examination you will be studying a case study that
has been prepared by the College Board. This case study involves a rather
substantial amount of C++ source code and it has many overloaded operators.
You will find that free functions are used heavily and the access of private data
with the free functions is done in a manner that is similar to the next program
example. In other words, pay attention because you will see this again.

// PROG2828.CPP
// This program demonstrates how to use a binary operator free
// function without using friends. This approach uses a new
// GetSavings function and operator+= function to access data.

#include <iostream.h>
#include <conio.h>
#include "APSTRING.H"
class Piggy
{
public:
Piggy(int N)
: Savings(N) { }
void operator+= (int N)
{ Savings += N; }
void ShowSavings(apstring Name) const;
int GetSavings() const
{ return Savings; }
private:
float Savings;
};

Piggy operator+(const Piggy &LHS, const Piggy &RHS);

void main()
{
clrscr();
Piggy Tom(200);
Piggy Sue(1400);
Piggy Larry(0);
Tom.ShowSavings("Tom");

Chapter XXVIII Overloaded Operators 28.58


Sue.ShowSavings("Sue");
Larry.ShowSavings("Larry");
Larry = Tom + Sue;
Tom.ShowSavings("Tom");
Sue.ShowSavings("Sue");
Larry.ShowSavings("Larry");
getch();
}

void Piggy::ShowSavings(apstring Name) const


{
cout << endl;
cout << Name << " has " << Savings << " dollars saved" << endl;
}

Piggy operator+(const Piggy &LHS, const Piggy &RHS)


{
Piggy Temp(0);
Temp += LHS.GetSavings();
Temp += RHS.GetSavings();
return Temp;
}

/***

Piggy operator+(const Piggy &LHS, const Piggy &RHS)


{
Piggy Temp(LHS);
Temp += RHS.GetSavings();
return Temp;
}

***/

PROG2828.CPP OUTPUT

Tom has 200 dollars saved

Sue has 1400 dollars saved

Larry has 0 dollars saved

Tom has 200 dollars saved

Sue has 1400 dollars saved

Larry has 1600 dollars saved

Chapter XXVIII Overloaded Operators 28.59


The solution is quite clever, and not that tricky. We start by implementing an
operator+= member function that uses a single parameter. Since it is a member
function we have no difficulty accessing member data. From there we move on to
the free operator+ function and pass two parameters, just like we have always
wanted to do, with nice protection for both sides of the binary operator. Now we
define a local Piggy object, initialized to zero. After that the LHS side is added,
followed by the RHS and we are in business.

Piggy operator+(const Piggy &LHS, const Piggy &RHS)


{
Piggy Temp(0);
Temp += LHS.GetSavings();
Temp += RHS.GetSavings();
return Temp;
}

Piggy operator+(const Piggy &LHS, const Piggy &RHS)


{
Piggy Temp(LHS);
Temp += RHS.GetSavings();
return Temp;
}

A second operator+ is shown as a slight variation on the same theme. The logic
is identical, but one program statement is avoided. The Temp object is
constructed with the LHS parameter. This invokes the copy constructor and
Temp will be a copy of LHS. A single call to operator+= then adds the RHS
and the function is ready to return the requested sum.

You need to realize that it is not just a matter of "beefing" up the left hand side
(LHS) with the same protection as the (RHS) that motivates the use of free
functions. There is another important issue involved that makes free functions
with two parameters very important. The concern is the nature of the operands,
specifically non-object operands.

Consider the situation of our Piggy class again, and look at the three different
possibilities that can occur with a binary operator. I am talking about the three
types of operands that can be used with a binary operator. Let us try to stick to
single-parameter operator functions and see what is happening. I am intentionally
showing the dot.function notation below each statement, to help clarify the
potential problem.

Chapter XXVIII Overloaded Operators 28.60


Two Objects

Sue = Tom + Larry;


Sue = Tom.operator+(Larry);

The LHS (Tom) is an object tied to the this pointer, and the RHS (Larry) is an
object that can be passed as the single paremeter. This situation does not cause a
problem for a single-parameter operator+ member function.

One Object on the LHS

Sue = Tom + 500;


Sue = Tom.operator+(500);

The LHS (Tom) is an object tied to the this pointer, and the RHS (500) can be
passed as a single parameter. This situation does not cause a problem for a single-
parameter operator+ member function.

One Object on the RHS

Sue = 500 + Tom;


Sue = 500.operator+(Tom); // no way

The LHS (500) is not an object and cannot be tied to the this pointer. The RHS
(Tom) can be passed as a single parameter, but this is only possible if there is an
object for the LHS. This situation causes major problems for a single-parameter
operator+ member function, and is a major reason for creating two-parameter free
overloaded operators.

28.12 Insertion and Extraction Operators

There are some special overloaded operator functions that can only be free
functions. You have used these operators since the beginning of time and I am
talking about the stream operators, often called chevrons. More formally the
input operator >> is called the extraction operator and the output operator << is
called the insertion operator.

It may well be desirable to overload these input/output operators with the use of
some class. You can then have the added benefit of customizing i/o operations to

Chapter XXVIII Overloaded Operators 28.61


the class that you are declaring. Now you do need to use these operators with
some special care because overloading stream operators requires two operands
and the operands belong to two different classes. This means that it is not
possible to make such a operator a member function of one class. You will need
to declare a free function and pass the required parameters to the function.

Let us start by looking at a program that provides output. Once again it is the
trusty Piggy class that helps us out here. We are so excited about savings that we
want to be able to display the amount of savings by simply using a statement like:

cout << Tom;

In such a case Tom is a member of the Piggy class and the result should be a
display of the amount of money that Tom has saved. Program PROG2829.CPP
demonstrates how to implement such an overloaded operator function.

// PROG2929.CPP
// This program demonstrates an overloaded insertion operator.
// It is not possible to implement this operator as a member
// function.

#include <iostream.h>
#include <conio.h>

class Piggy
{
public:
Piggy(int N)
: Savings(N) { }
float GetSavings() const
{ return Savings; }
private:
float Savings;
};

ostream &operator << (ostream &OS, const Piggy &Obj);

void main()
{
clrscr();
Piggy Tom(200);
cout << "Tom: " << Tom << endl;
Piggy Sue(1400);
cout << "Sue: " << Sue << endl;
getch();
}

Chapter XXVIII Overloaded Operators 28.62


ostream &operator << (ostream &OS, const Piggy &Obj)
{
return OS << Obj.GetSavings();
}

PROG2829.CPP OUTPUT

Tom: 200
Sue: 1400

The actual function body is quite short. The public member function GetSavings
is used to access the private data Savings. The value of Savings is then inserted
into the output stream for display.

Do you notice that there are two different classes passed as parameters. There is
one object (Obj) of the Piggy class and there is also one object (OS) of the
ostream class. These two classes cannot live together declared inside the same
class. We must then either use the friend function approach or use some helper
function to access the data. Either way we have to use a free function.

The final program example adds the extraction operator. We can now do both
input and output with objects of our trusty Piggy class. The last example of this
chapter, program PROG2830.CPP demonstrates how to overload the extraction
operator.

You may feel relieved that this chapter has reached its final example. However,
there is still some future business to be handled with overloaded operators. You
will need to learn some other concepts first and then we will return to this topic.
Overloaded operators are powerful and help to make C++ a popular language. It
is not an easy topic, and it will take some digestion time to feel comfortable.

// PROG2830.CPP
// This program adds a free overloaded extraction operator
// function to the previous program.

#include <iostream.h>
#include <conio.h>

class Piggy
{
public:

Chapter XXVIII Overloaded Operators 28.63


Piggy(int N)
: Savings(N) { }
float GetSavings() const
{ return Savings; }
void PutSavings(float S)
{ Savings = S; }
private:
float Savings;
};

ostream &operator << (ostream &OS, const Piggy &Obj);


istream &operator >> (istream &IS, Piggy &Obj);

void main()
{
clrscr();
Piggy Tom(200);
cout << "Tom: " << Tom << endl;
Piggy Sue(1400);
cout << "Sue: " << Sue << endl;
cout << "Enter two values ";
cin >> Tom >> Sue;
cout << "Tom: " << Tom << endl;
cout << "Sue: " << Sue << endl;
getch();
}

ostream &operator << (ostream &OS, const Piggy &Obj)


{
return OS << Obj.GetSavings();
}

istream &operator >> (istream &IS, Piggy &Obj)


{
float Temp;
IS >> Temp;
Obj.PutSavings(Temp);
return IS;
}

PROG2830.CPP OUTPUT

Tom: 200
Sue: 1400

Chapter XXVIII Overloaded Operators 28.64


Enter two values 1000 2000
Tom: 1000
Sue: 2000

Overloaded Stream Functions

The stream insertion operator << and the stream


extraction operator >> can be overloaded.

Two operands are required for stream operators.


One operand will be an object of a stream class and
the other operand will be an object of the class used
with the overloaded stream operators.

Since the two operands of the stream operators are from


two different class, it is not possible to declare an
overloaded stream operator as a member function.

Overloaded stream operators must be free functions


that either are implemented as friend functions of a class
or use some accessing helper function.

Chapter XXVIII Overloaded Operators 28.65

You might also like