You are on page 1of 61

Chapter XXVI

Focus on Object Oriented Programming


Encapsulation

Chapter XXVI Topics


26.1 Introduction

26.2 What is OOP?

26.3 Header and Implementation Files

26.4 Creating Compiled Libraries

26.5 Preprocessors #ifndef and #define

26.6 Review of OOP Basics

26.7 Classes and Include Files

26.8 Understanding the “this” Pointer

26.9 List Class Case Study

26.10 Constant Member Functions

26.1 Introduction

Chapter XXVI Focus on OOP, Encapsulation 26.1


A long time ago - measured in student time - you were introduced to Object
Oriented Programming, known more commonly as OOP. You learned some new
vocabulary, investigated some program examples, and did a few program
assignments. You also learned about the apvector, apstring and the apmatrix
classes. These classes did not teach you as much about OOP, as it gave you
practice using special data types based on OOP features. If you feel that your
OOP knowledge is skimpy and suffered from steady memory erasure, due to
insufficient reinforcement, you are quite right. But hang on, because you will be
up to your nostrils, and then some, in OOP for many, many chapters.

Each one of the OOP chapters will be relatively short, and focus on one particular
aspect of OOP. This approach is a little artificial. Each chapter may almost
appear that other features of OOP do not exist. This approach is intentional and
by now you must realize that I prefer teaching with a focused approach on one
particular concept at one time.

Object Oriented Programming can easily be overwhelming, and requires a


considerable amount of digestion time to acquire a comfort level. Is OOP then so
difficult? No, it really is not that hard, conceptually speaking. The problem is
that there are so many different concepts that all get tossed together in one giant
mixed salad. By the time that you finish Exposure C++, Part III, I hope that you
will feel comfortable about OOP, and hopefully you will like the many powerful
features that are possible with this programming style.

This chapter will focus on encapsulation. The next chapter will look at all the
details of constructors and destructors. When all these individual concept
chapters are done, we put everything together and observe a more global picture.
Now, it is not possible to look at individual concepts strictly in a vacuum. Each
program example still needs all the minimal program items necessary to make the
program compile and execute. The point is that the focus of how to do some
features a variety of ways is concentrated in one chapter. So this chapter will put
member data and member functions under a microscope. Did you forget what a
member function is? No problem . . . there are lots of examples in this chapter to
remind you. Furthermore, because this is the first OOP chapter, a general
introduction to object oriented programming will be provided as well.

This chapter also covers a brief topic of include files and preprocessors. You will
find that the topic fits nicely with member functions, as it explains how to
organize different components of your program and where to place them.

26.2 What is OOP?

Chapter XXVI Focus on OOP, Encapsulation 26.2


Let me try a more thorough crack at an OOP definition than you received at the
first introduction. After all, you are in Chapter XXVI; you are more sophisticated
now, and you can handle more vocabulary.

Object Oriented Programming Definition

Object Oriented Programming is a style of programming


that incorporates program development in a language
with the following three OOP traits:

 Encapsulation
 Polymorphism
 Inheritance

It is easy to toss vocabulary around. After all, encapsulation, polymorphism and


inheritance are household words in your environment. It would be nice if we
could learn computer science, or any other discipline, without the intimidating
language, but that is not possible. The vocabulary exists; it gets used, and you
will be at a handicap if you do not understand what people are talking about. I
will take each one of the three OOP corner stone features and give a brief,
introductory explanation.

Encapsulation

Encapsulation is what you learned and practiced back in Chapter XVII. In that
chapter we took a data structure that was minding its own business storing data,
and placed the functions, which manipulated the data, inside the data structure
declaration. OOP uses neat packages of data (often called attributes), combined
with the functions that act upon the data (often called actions or methods). This
package of attributes and actions is the main character of this story, the object.
Another word for package can be container or capsule. Placing everything inside
a capsule is encapsulation.

Encapsulation Definition

Chapter XXVI Focus on OOP, Encapsulation 26.3


Encapsulation is the process of placing a data structure’s
data (attributes) with the functions (actions) that act upon
the data inside the same data structure.

Polymorphism

Polymorphism means many-forms. You have already seen various examples of


polymorphisms. Operators like + have been used for different operations like
arithmetic addition and string concatenation. You have also seen that the same
function name can be used for different functions. Another term that is
commonly used is overloading. We talk about overloaded operators and
overloaded functions when the operator or the function has multiple applications.
The significance of this feature is that overloading does not just exist for various
operators (like +) as part of the language, overloading can be controlled by
programmers. The power of polymorphism in C++ is the ability of programmers
to add their own new functions and operators that are overloaded.

Polymorphism Definition

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.

Inheritance

Inheritance will be quite a different story. The OOP features, encapsulation and
polymorphism could be explained by various examples that you had seen in prior
chapters. Inheritance is a totally new concept, that will be briefly mentioned and
then allowed to rest until a complete chapter on the subject will describe the
details of inheritance. Go back to your Geometry days. For many students this is
not a long distance trip to the past. One important feature of Geometry is its use
of prior material. After a quadrilateral has been defined, its definition can be used
to simplify the definition of a parallelogram. All the features of a parallelogram

Chapter XXVI Focus on OOP, Encapsulation 26.4


in turn are part of a rectangle, plus additional features. The rectangle in turn can
then be used to define a square. Defining new concepts in terms of previously
defined concepts is part of mathematical logic, and avoids tedious definitions that
would otherwise becomes very long. Geometry does not use the term inheritance
to explain the manner in which prior definitions are used. However, you can
think of a rectangle inheriting all the properties of a parallelogram. The rectangle
inherits four sides from the parallelogram, and there are two pairs of parallel
sides. After the inherited features are established, the rectangle now adds its own
special features, which are four right angles. If we continue this process we can
continue to use inheritance for the definition of a square. The square inherits all
the features of a rectangle and adds that all four sides are the same length. Think
of inheritance as an excellent tool to prevent continuously reinventing the wheel.
What has been described so far has been the concept of inheritance, and that is all
that will be done here. How inheritance is accomplished in C++ will be a topic
for a later chapter in the Object Oriented Programming saga.

Inheritance Definition

Inheritance is the process of using features (both attributes


and actions) from an established higher class. The higher
class is called the base class.

APCS Examination Alert

The topic of inheritance is not currently included in the APCS


C++ subset for examination purposes. This topic is included
in Exposure C++ with the believe that an introductory
knowledge of inheritance will help students gain a more
complete picture of the scope and power of OOP.
Additionally, the topic is included because it is anticipated
that inheritance will probably be part of the APCS C++
subset as teaching computer science at the high school
level continues to evolve.

26.3 Header and Implementation Files

Chapter XXVI Focus on OOP, Encapsulation 26.5


This section may seem odd. You are ready to do some serious OOP plunging-in
and now suddenly this section on a bunch of header and implementation files
pops up. The logic of this section is based on organization. As programs become
bigger, organization is vital. A brief peek at virtually any existing program will
confirm that the days of single-executable-file-handling-the-job are over. Sure,
your program assignments were simple, but even your simple assignments
contained a program source file and a second utility library that was included at
compile time. The main purpose of this chapter is to review object oriented
programming and take a closer look at the member functions of an object. This
will require that all the bits and pieces of your source code, class declarations, and
other program features are nicely organized in a manner that makes both
debugging and updating manageable.

You have used the “include file concept” for some time with a library of utility
functions. Right now you need to review, and understand this include business in
a more thorough manner and truly understand what is involved. For starters look
at program PROG2601.CPP, which will be used for a variety of demonstrations.
This program calls three functions, each of which basically announces its
presence. There is also a small Skip function that is used by the other functions.

// PROG2601.CPP
// This program demonstrates a few simple functions
using
// prototypes before the main function and the function
// implementations below the main function body.

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

void Skip(int N);


void Function1();
void Function2();
void Function3();

void main()
{
clrscr();
Function1();
Function2();
Function3();
getch();
}

Chapter XXVI Focus on OOP, Encapsulation 26.6


void Skip(int N)
{
int K;
for (K=1; K<=N; K++)
cout << endl;
}

void Function1()
{
Skip(2);
cout << "THIS IS FUNCTION 1" << endl;
}

void Function2()
{
Skip(2);
cout << "THIS IS FUNCTION 2" << endl;
}

void Function3()
{
Skip(2);
cout << "THIS IS FUNCTION 3" << endl;
}

PROG2601.CPP OUTPUT

THIS IS FUNCTION 1

THIS IS FUNCTION 2

THIS IS FUNCTION 3

There were no big surprises in this first program example. Everything was done
in a straightforward manner. The second program example will now take the first

Chapter XXVI Focus on OOP, Encapsulation 26.7


example and use several include files. The first file, called INCLUDE2.H, is a
small file of function prototypes. These function prototypes are all function
headings and it is customary to call such an include file, a header file. Header
files normally use the *.H suffix. The second file, called INCLUDE2.CPP,
holds the implementations of the function prototypes in the header file.

// PROG2602.CPP
// This program demonstrates using two include files.
// One is a header file of prototypes and the other is
the
// implementation file of function definitions.

#include "INCLUDE2.H"

void main()
{
clrscr();
Function1();
Function2();
Function3();
getch();
}

#include "INCLUDE2.CPP"

Pay particular attention to the location of the #include preprocessor statements.


The two statements are in the exact same location as the program statements used
to be that are being “included.” The header file, INCLUDE2.H, is included
before the main function, in the location where the function prototypes used to be.
The implementation file, INCLUDE2.CPP, is included after the main function.

// INCLUDE2.H
// Include file for PROG2602.CPP
// This file contains the function prototypes.

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

void Skip(int N);


void Function1();

Chapter XXVI Focus on OOP, Encapsulation 26.8


void Function2();
void Function3();

#include Preprocessor

The #include preprocessor may be placed anywhere in a


program. At the location of the include statement, the source
code of the included file will be compiled. Place #include so
that code will be compiled in the proper program sequence.

// INCLUDE2.CPP
// Include file for PROG2602.CPP
// This file contains the function declarations of the
// prototypes in the INCLUDE2.H header file.

void Skip(int N)
{
int K;
for (K=1; K<=N; K++)
cout << endl;
}

void Function1()
{
Skip(2);
cout << "THIS IS FUNCTION 1" << endl;
}

void Function2()
{
Skip(2);
cout << "THIS IS FUNCTION 2" << endl;
}

void Function3()
{
Skip(2);
cout << "THIS IS FUNCTION 3" << endl;
}

Chapter XXVI Focus on OOP, Encapsulation 26.9


PROG2602.CPP OUTPUT

THIS IS FUNCTION 1

THIS IS FUNCTION 2

THIS IS FUNCTION 3

The next program example shows a slight variation of the previous program,
which used two #include preprocessors in the main driving program. Program
PROG2603.CPP shows only one #include statement in the main driving
program. Only the header file, INCLUDE3.H, is included in the main program.
The program is identical in result to the previous program. The same result is
achieved by having an #include statement in the header file that compiles the
function implementation at the correct location.

// PROG2603.CPP
// This program is almost identical to the previous
program.
// In this version only the INCLUDE3.H (header) file is
included.
// The INCLUDE3.CPP file of function definitions is
included
// by the header file.

#include "INCLUDE3.H"

void main()
{
clrscr();
Function1();
Function2();
Function3();
getch();
}

Chapter XXVI Focus on OOP, Encapsulation 26.10


// INCLUDE3.H
// Include file for PROG2603.CPP
// This file contains the function prototypes.
// Furthermore, this file includes INCLUDE3.CPP, which
are
// the function definitions.

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

void Skip(int N);


void Function1();
void Function2();
void Function3();

#include "INCLUDE3.CPP"

Notice how the header file includes INCLUDE3.CPP at the end of the file, below
the function prototypes. This will result that the implementation file will be
compiled at that location. Is this wrong? After all, you have been accustomed to
a system of prototypes, followed by the main function, which is then followed by
the function implementations.

If you understand this include business correctly, it will result in the main
function coming dead last now in the sequence. This sequence is still perfectly
correct. As long as the function prototypes come before the function
implementations and the main function everything is in order.

// INCLUDE3.CPP
// Include file for PROG2603.CPP
// This file contains the function declarations of the
// prototypes in the INCLUDE3.H header file.

void Skip(int N)
{
int K;
for (K=1; K<=N; K++)
cout << endl;
}

Chapter XXVI Focus on OOP, Encapsulation 26.11


void Function1()
{
Skip(2);
cout << "THIS IS FUNCTION 1" << endl;
}

void Function2()
{
Skip(2);
cout << "THIS IS FUNCTION 2" << endl;
}

void Function3()
{
Skip(2);
cout << "THIS IS FUNCTION 3" << endl;
}

PROG2603.CPP OUTPUT

THIS IS FUNCTION 1

THIS IS FUNCTION 2

THIS IS FUNCTION 3

Perhaps you are quite confused about just exactly what program statements go
where, and why. You have now gone quite some time in a well established ritual
sequence of prototypes, main function, function implementations. Apparently
now you are told that this is not really so significant anymore and the sequence
can be altered. It is very important that you clearly understand what is flexible in
a program sequence and what is not. In other words, some sequencing is done
because it is required by C++ syntax, and other sequencing is done because of
convention. Which is which?

Program Sequence Rules

Chapter XXVI Focus on OOP, Encapsulation 26.12


All user-created identifiers must be declared before
they are used in any program statement.

Function prototypes are placed before the main function and


before any function implementations. This sequencing allows
functions to be called by any other function in any order.

The main function is normally positioned ahead of the


function implementations to provide an immediate view
of the global flow of the program at the top of the source code.
This convention also makes correct parameter matching easier.

The rules in the summary box above seem to disagree with the earlier program
that placed the function implementations above the main function. Now if you
really paid close attention you will note that the program did follow the sequence
rules. C++ does not require that the main function comes before the function
implementations. That is just convention for ease of viewing the program. Now
if source code is placed in an include file, the viewing of the main program source
code is not altered. In other words, with all this include business, the first
program statements to be seen are those in the main function. All other source
code is placed in one or more include files.

This page is very significant because writing a program requires that syntactical
program language rules are obeyed. At the same time, program writing should
follow conventions that are established for ease of program development. Include
files can be tricky in this respect because major sections of program code are no
longer in direct view. You must have a clear picture, in your head or on paper, of
the complete source code sequence. This becomes especially important when the
main program includes a file, which in turn includes another file.

It is possible that you never quite understood why all this include file stuff was
done. You did start using include files quite some time ago, but it is common for
students to develop certain program routines without appreciating the purpose for
such routines. A few reminders will follow here to help explain why this business
is used in the first place.

It may be simpler to think of a program that does not use include files. Imagine a
sizable program of 5000 lines of source code. This is small in the software
application world, but quite large for the average high school or college student.
At 25 lines per screen, a program of this size will use up 200 monitor screens.

Chapter XXVI Focus on OOP, Encapsulation 26.13


Scrolling up and down 200 screens for writing, testing and debugging is very,
very tedious.

OK, suppose you are not concerned about huge amounts of tedious code that must
be scrolled past to get to the desired location. Are there still other reasons? You
bet, and one of them is ease of debugging. Your program may contain routines
that have been solidly tested in the past, and there may be sections in the program
that are finished and likewise have undergone thorough testing. It is very
convenient to take this code and remove it from view. The code is finished and
only occupies space. Perhaps a desk analogy helps here. A student, a teacher, an
engineer, a businessman or any other person uses a desk to do work. The desk is
cluttered with papers that need to be finished, edited, graded, whatever. Now
does it not make sense that groups of papers that are finished are grouped
together, placed in a file and placed somewhere away from the desk? Leave the
desk as empty as possible with only those papers left that still require attention.

26.4 Creating Compiled Libraries

This will be an odd section. A concept will be mentioned here, but it will not be
explained. I am talking about compiled libraries. Understanding this business
requires that we back-up and look what happens when we eagerly try to compile
our programs. You have probably noticed that a variety of files are created that
you never remembered saving. Files with *.OBJ endings pop up all over the
place. There is a tendency to speak of compiling a program, but there is more
than compiling that is happening. When you press <F9> or any other appropriate
key combination or mouse clicks to get your program ready, a sequence of
processes follows. This sequence more than translates your C++ source code into
binary machine code that can be executed.

The Sequence of Creating an Executable Program

The compiler checks the syntax of your source code. If the


syntax is correct, an object file of binary code will be created.

Chapter XXVI Focus on OOP, Encapsulation 26.14


If the code has some error(s) you will get error messages.

Your program includes libraries of C++ routines like iostream


that are precompiled. In the next stage, called linking, your
binary coded object file will be linked to the necessary object
files of appropriate libraries.

After the linking process is finished, you will have an *.EXE


file that can be executed.

Is there an advantage to all this compiled library stuff? Yes, and the advantage is
simply speed. Code that has been thoroughly tested can go one step further than
to be placed in an include file, it can be compiled into an binary code object file.
This step will avoid the need to recompile the code a second, or multiple more,
time(s) during program development. For small programs this is all of no
significant consequence. Very large programs pay a considerable penalty in
speed if unnecessary compiling is repeated over and over again. Just imagine a
one million line program in its final stage. A team of programmers is finishing
the final 10,000 lines of code and 990,000 lines of code are recompiled each time
that the final stage is tested.

The compiled libraries provided by your C++ software also serve a portability
purpose. The C++ language is meant to be portable with many different
computing environments. One major problem is input/output (I/O) routines. A
program can be portable across various platforms and each platform carries its
own library of input/output libraries to be linked with the program.

Now this is all lovely and here comes the bad news. You will not be shown how
to create a compiled library. Any attempt to do that in this chapter will probably
be pretty sad. The whole process of creating a compiled library is not only
different with different C++ software companies, it is also different between
different versions of the same compiler. With some C++ versions the compiled
library process is very easily accomplished. Users of such software wonder what
the fuzz is about. There are also other C++ packages that make creating compiled
libraries quite difficult. Any attempt to explain the process here would be
extremely system dependent without guarantees that it will work.
Compiling source code files with include files, as shown in this chapter, is very
reliable and works across all the different C++ versions. The speed of today’s
modern computers is also such that compiling is very fast and the penalty of
recompiling finished routines is only a problem when the programs becomes quite
large. I have personally worked with a C++ program in the neighborhood of
8,000 lines and found that the compile time was not objectionable at all. This
does not mean that you should not use compiled libraries. By all means check the

Chapter XXVI Focus on OOP, Encapsulation 26.15


reference manual of your particular C++ version and determine whether this is
done naturally or whether it is not worth the trouble. You be the judge.

Using Compiled Libraries or Not?

Creating compiled object files out of finished code reduces


compile time during program development.

The penalty of recompiling finished code becomes a major


factor in the development of very large application programs.

Creating compiled object files is very specific to a C++ version


and can be very natural or quite tedious. You need to check
the reference manuals of individual C++ versions for specific
information on how to create a compiled library.

26.5 Preprocessors #ifndef & #define

Include files can create a headache all of their own. It was mentioned that you
need to have a picture in your head about the organization of your program, or
better yet, a written plan that displays the global flow of your program logic.
With the best intentions it will not take long before it really becomes confusing if
some support file of library routines has been included or not. This complexity
grows when you create compiled libraries that require the inclusion of all support
files to create an object file. This brings up a curious problem. What happens if
the same file is included more than once? How does the compiler react to such an
event. Program PROG2604.CPP intentionally includes the INCLUDE4.H1
header file twice. After the program source code, the output example shows how
the compiler reacts to this type of programming.

// PROG2604.CPP
// This program demonstrates the problems that can
occur when

Chapter XXVI Focus on OOP, Encapsulation 26.16


// the same file is included at several different
program
// locations.

#include <iostream.h>
#include <conio.h>
#include "INCLUDE4.H1"
#include "INCLUDE4.H2"

void main()
{
clrscr();
Function1();
Function2();
Function3();
getch();
}

// INCLUDE4.H1
// This include file is used by PROG2604.CPP
// This file contains the Skip function that will used
by
// all the other functions.

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

void Skip(int N)
{
int K;
for (K=1; K<=N; K++)
cout << endl;
}

The INCLUDE4.H1 file is a very short file that contains a few include statements
and a Skip function. Note that the main driver program includes IOSTREAM.H
and so does the INCLUDE4.H1 file. In this program you see multiple examples
of including the same file at different locations. Will the compiler accept this?

Chapter XXVI Focus on OOP, Encapsulation 26.17


// INCLUDE4.H2
// This file contains the prototypes of PROG2604.CPP
// It includes the INCLUDE4.H1 with the Skip function,
// as well as the INCLUDE4.CPP file of function
definitions.

#include "INCLUDE4.H1"

void Function1();
void Function2();
void Function3();

#include "INCLUDE4.CPP"

// INCLUDE4.CPP
// Include file for PROG2604.CPP
// This file contains the function declarations of the
// prototypes in the INCLUDE4.H2 header file.

void Function1()
{
Skip(2);
cout << "THIS IS FUNCTION 1" << endl;
}

void Function2()
{
Skip(2);
cout << "THIS IS FUNCTION 2" << endl;
}

void Function3()
{
Skip(2);
cout << "THIS IS FUNCTION 3" << endl;
}

PROG2604.CPP OUTPUT

There is no program execution. The program will have the following


compile error:

Error INCLUDE4.H1 12: Body has already been defined

Chapter XXVI Focus on OOP, Encapsulation 26.18


function
’Skip(int)’

The error message is more significant than you may think. The message states
that Skip already has been defined. This statement is certainly appropriate but the
INCLUDE4.H1 is included after IOSTREAM.H. What is the point? There is
no indication that there is any problem with the IOSTREAM.H file, yet that file
is included twice as well. You may think that the compiler stopped when it first
encountered the problem and therefore you do not see a later message about
IOSTREAM.H. That is a good theory but the compiler is extremely sequential
in reporting problems that it encounters and IOSTREAM.H is included first and
should be the first candidate for some duplicate identifier type of error message.

Are you drawing the correct conclusion that something was done to the
IOSTREAM.H file that prevented the problem, and we need to do something of
the same nature to our included files? This is a wise conclusion. Now how can
this type of problem be solved?

Consider the following situation in a high school. Some emergency has happened
and the school principals are visiting every class to talk to students. There is not
enough time to create an organized schedule of who visits which class. At the
same time classes should not be unnecessarily interrupted. At the conclusion of a
visit each principal tapes a note card on the visited classroom door. Any other
principal who observes the card, knows that the class has been visited. Such a
system is very simple, yet very effective. It is possible to use a similar system
with C++ by “marking” a file with a system that can determine if the file has been
included or not. C++ provides some special purpose preprocessors to accomplish
this task. Program PROG2605.CPP uses these special processors. Look at the
program first, and satisfy yourself that the previous problem has now been solved.
The special features of this program will be explained afterwards.

// PROG2605.CPP
// This program cures the problem of the previous
program.
// The include file now uses the #ifndef preprocessor
to
// alert the compiler that the file has already been
compiled.

#include <iostream.h>
#include <conio.h>
#include "INCLUDE5.H1"
#include "INCLUDE5.H2"

Chapter XXVI Focus on OOP, Encapsulation 26.19


void main()
{
clrscr();
Function1();
Function2();
Function3();
getch();
}

// INCLUDE5.H1
// This include file is used by PROG2605.CPP
// This file uses the #ifndef preprocessor to prevent
compiling
// errors, due to multiple Skip function definitions.

#ifndef _INCLUDE5_H1
#define _INCLUDE5_H1

#include <iostream.h>
#include <conio.h>
void Skip(int N)
{
int K;
for (K=1; K<=N; K++)
cout << endl;
}

#endif

// INCLUDE5.H2
// This file contains the prototypes of PROG2605.CPP
// It includes the INCLUDE5.H1 file with the Skip
function,
// as well as the INCLUDE5.CPP file of function
definitions.

#include "INCLUDE5.H1"

void Function1();
void Function2();
void Function3();

#include "INCLUDE5.CPP"

Chapter XXVI Focus on OOP, Encapsulation 26.20


// INCLUDE5.CPP
// Include file for PROG2605.CPP
// This file contains the function declarations of the
// prototypes in the INCLUDE5.H2 header file.

void Function1()
{
Skip(2);
cout << "THIS IS FUNCTION 1" << endl;
}
void Function2()
{
Skip(2);
cout << "THIS IS FUNCTION 2" << endl;
}
void Function3()
{
Skip(2);
cout << "THIS IS FUNCTION 3" << endl;
}

PROG2605.CPP OUTPUT

THIS IS FUNCTION 1

THIS IS FUNCTION 2

THIS IS FUNCTION 3

C++ has a variety of conditional preprocessors. A preprocessor provides special


instructions to the compiler, such as including a file. It is also possible to provide
a conditional preprocessor which can gives special compile instructions for a
specified program segment. Now consider the following program segment:

#ifndef QWERTY
#define QWERTY

void HappyFunction()
{
cout << ”I am happy” << endl;

Chapter XXVI Focus on OOP, Encapsulation 26.21


}

#endif

The preprocessor #ifndef means if QWERTY is not defined do what follows


until you encounter #endif. The identifier QWERTY can be any name. All
identifiers are automatically defaulted to not defined and they stay not defined
until a statement like #define QWERTY marks the identifier defined.

What does all of this mean? The first encounter with the statement ifndef
QWERTY starts out with QWERTY not defined. If the program segment is
included in a file, this will mean that all the program statements will be compiled.
Do notice that immediately after the #ifndef follows another preprocessor, which
is #define. This processor will insure that QWERTY becomes defined. This
change of status with QWERTY means that the next time the if statement is
false. Now just like any other conditional statement, the program block following
the if is only executed when the conditional statement is true. Now let us look at
the INCLUDE5.H1 file and see just what that means.

// INCLUDE5.H1
// This include file is used by PROG2605.CPP
// This file uses the #ifndef preprocessor to prevent
compiling
// errors, due to multiple Skip function definitions.

#ifndef _INCLUDE5_H1
#define _INCLUDE5_H1

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

void Skip(int N)
{
int K;
for (K=1; K<=N; K++) cout << endl;
}

#endif
The main program PROG2605.CPP includes INCLUDE5.H1. The first time
that the file is included, the ifndef statement is false. Remember that any
identifier may be used, but by convention the identifier is the name of the file
preceded by an underscore character. Since _INCLUDE5_H1 is not defined, the
program block after the if and the endif is entered.

Chapter XXVI Focus on OOP, Encapsulation 26.22


The next statement encountered by the compiler is the statement #define
_INCLUDE5_H1, which defines or marks the identifier _INCLUDE5_H1. The
compiler now continues and translates the source code, which happens to be the
Skip function.

After this short file, the compiler continues on its merry way and comes on the
statement #include INCLUDE5.H2, which happens to have its own statement
that is #include INCLUDE5.H1. The compiler now makes a second visit to the
humble INCLUDE5.H1 file. But now there is a difference. _INCLUDE5_H1 is
now defined, which makes the #ifndef statement false, and the compiler skips to
the #endif to continue. In this case that means the end of the file, and the result is
that the Skip function is not encountered a second time.

Should every file have an #ifndef statement to avoid duplicate inclusion? Perhaps
this is a good idea, but in practice you will find the preprocessors with the header
files only. The logic of this convention is that header files should pull in the
implementation files - especially when compiled libraries are used - and this only
happens when the header file is compiled. In other words, any attempt to include
the header file a second time will fail because of the #ifndef protection. This in
turn prevents the implementation file to be included a second time.

Conditional Preprocessors Avoids Using Duplicate Files

Use the #ifndef preprocessor at the start of a file to insure


that the file will only be compiled once.

Use the #ifndef identifier preprocessor followed by

Chapter XXVI Focus on OOP, Encapsulation 26.23


the #define identifier preprocessor.

By convention the file name for the if identifier is


used with under score characters. For instance, a file
called UTILITY.H will use the following statements:

#ifndef _UTILITY_H
#define _UTILITY_H

Make sure to finish the file with the #endif preprocessor:

#endif

As you can see, the include file business has its share of complexity. There is not
one thou shalt do it only this way approach. There are a variety of approaches
that will work correctly. Knowing what works and does not work requires that
you understand the proper program sequence requirements, and then make sure
that your include files obey the sequence needed by your program.

You may find it interesting to look at the header files IOSTREAM.H and
CONIO.H, provided by your compiler. These files will have considerable
complexity, but you will notice that the conditional preprocessors are used. Even
the very short BOOL.H file uses conditional preprocessors. Short files that may
be included at many stages of a program are excellent candidates for accidental
inclusion at more than one location. Make sure to protect these files. I will finish
this section by showing you the brief BOOL.H file. Note that once again the
identifier that is used is based on the name of the file. Just keep in mind that this
name could be AARDVARK and there is not any requirement that this name has
anything to do with the external filename.

// USER-DEFINED BOOLEAN TYPE


// TO BE USED WITH AP CLASSES

#ifndef _BOOL_H
#define _BOOL_H

typedef int bool;


const int false = 0;

Chapter XXVI Focus on OOP, Encapsulation 26.24


const int true = 1;

#endif

26.6 Review of OOP Basics

This section will be review. Everything shown here was already presented in an
earlier chapter. Now you will probably not require this review, but you know not
every student is as clever and as cerebrally gifted as you are.

Your first concern with OOP is encapsulation, which is the creation of a data type
that combines the attributes (data) with the actions that act upon the data
(member functions). The main purpose of this chapter is to investigate the
various features of the member functions that are part of an object.

Take a look at program PROG2606.CPP, on the next page, which features a


short program with a Car class. This class stores data about the year that the car
was made and the value of the car. The member functions of the class get data
values and display the data values. Everything is pretty straight forward.

// PROG2606.CPP
// This program reviews the basic syntax of creating a class
// with member functions defined after the main function.

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

class Car
{

Chapter XXVI Focus on OOP, Encapsulation 26.25


private:
int Year;
double Value;
public:
Car();
~Car();
void GetData();
void ShowData();
};

void main()
{
clrscr();
Car Jeep;
Jeep.GetData();
Jeep.ShowData();
getch();
}

Car::Car()
{
cout << endl << endl;
cout << "CAR IS CREATED" << endl;
Year = 0;
Value = 0;
}

Car::~Car()
{
cout << endl << endl;
cout << "CAR IS FINISHED" << endl;
getch();
}

void Car::GetData()
{
cout << endl << endl;
cout << "Enter car year ===>> ";
cin >> Year;
cout << "Enter car value ===>> ";
cin >> Value;
}

void Car::ShowData()
{
cout << endl << endl;
cout << "CAR YEAR: " << Year << endl;
cout << "CAR VALUE: " << Value << endl;
}

PROG2606.CPP OUTPUT

CAR IS CREATED

Chapter XXVI Focus on OOP, Encapsulation 26.26


Enter car year ===>> 1998
Enter car value ===>> 25000

CAR YEAR: 1998


CAR VALUE: 25000

CAR IS FINISHED

Different parts of the program will be used to review vocabulary and syntax that
you need to know when you create and use objects. The details of the program
will not get a whole lot of explanation. If this review does not make much sense,
you may need to go back to the earlier chapter where this object oriented
programming material was first introduced.

The Class and Object Difference

Remember the difference:

A class is a data type.

An object is a variable of a certain class.

There is some confusion about the word object. We talk about object oriented
programming, and we also talk about creating objects. Yet a new object data type
is declared with the class reserved word. This confusion is due to the fact that the
word object is used to describe the style of programming and the features that are
used. Object has both a generic meaning and a specific meaning. It is a little like
Kleenex, which can mean tissue, and it can also mean a brand of tissue. It is not
that big a deal. Just make sure that you use the program features correctly and
you will be fine.

Class Syntax

Chapter XXVI Focus on OOP, Encapsulation 26.27


Start with the reserved word class and declare every element
of the class in the following manner:

class Car // declares the class Car


{
private: // starts the private segment of Car
int Year; // data field
double Value; // data field
public: // starts the public segment of car
Car(); // constructor
~Car(); // destructor
void GetData(); // member function prototype
void ShowData(); // member function prototype
}; // remember to finish with a ;

Data and functions in the private segment of the car class


are only accessible by member functions of the car class.

Data and functions in the public segment are accessible


anywhere in the scope of the defined object.

Unless functions are very short, normally only the prototypes of the member
functions are placed inside the class declaration. The syntax of member functions
is almost the same as any other function, except for the function heading. The
heading must be different so that the compiler recognizes the function as a
member of a class and not some global function implementation.

The scope resolution operator, which is a C++ operator with two adjacent
colons ( :: ), is used in the declarations of member functions to identify where the
function belongs. Do not use the scope resolution operator if a complete function
declaration is placed inside a class. The compiler has no difficulty determining
that a function declaration is part of a class when a function, in its entirety, is
positioned inside a class. It is precisely when a function definition sits by itself
somewhere in the program source code that the compiler must have some means
to identify the function. The compiler must know that this is not some global
function swinging in a breeze, but additional information for a class positioned
earlier in the program source code.
Member Function Syntax

Chapter XXVI Focus on OOP, Encapsulation 26.28


Member functions use the scope resolution operator ( :: )
to identify the function as a member of a specified class.
The general syntax is:

void or type Class-Identifier :: Function-Identifier

void Car::GetData()
{
cout << endl << endl;
cout << "Enter car year ===>> ";
cin >> Year;
cout << "Enter car value ===>> ";
cin >> Value;
}

Most classes contain some special functions that have their own name, the
constructor and the destructor. It is not the purpose of this chapter to provide
details for constructors and destructors. That topic will be the focus of the next
chapter. In this chapter, constructors and destructors will be handled in a very
minimal manner. Constructors and destructors are functions inside a class, so
they certainly are member functions. However, as a rule when member functions
of a class are mentioned, they are kept separate from the special constructor and
destructor functions.

The constructor and destructor are quickly recognized because the function
identifier has the same name as the class identifier. Furthermore, constructor and
destructor function headings do not have any data type or void preceding the
function name. The destructor has a tilde ( ~ ) placed in front of its identifier.

The constructor is a function that is automatically called at the moment, or instant


that a new object is defined, created or constructed. Normally, the constructor
initializes the data of a new object and performs other tasks that are necessary for
the successful use of a new object. The destructor function is automatically called
when the object leaves its scope. The destructor performs clean-up jobs that need
to be completed after the object is no longer needed. A great many additional
details about all this constructor and destructor business will be provided in the
next chapter. So have patience. And if you do not have patience, go ahead and
read the next chapter.

Constructors and Destructors

The syntax of a constructor and destructor is like a member

Chapter XXVI Focus on OOP, Encapsulation 26.29


function without a void or data type preceding the function
identifier. Both the constructor and destructor use the same
function identifier as the class. The destructor is distinguished
by a tilde ( ~ ). The constructor and destructor functions must
always be in the public segment of the class.

Prototype examples inside the class declaration:

public:
Car();
~Car();

Constructor implementation example:


Car::Car()
{
cout << endl << endl;
cout << "CAR IS CREATED" << endl;
Year = 0;
Value = 0;
}

Destructor implementation example:

Car::~Car()
{
cout << endl << endl;
cout << "CAR IS FINISHED" << endl;
getch();
}

The constructor function is automatically called at the instant


that a new object is defined.

The destructor function is automatically called when the


object leaves its scope.

Declaring a user-created class with the reserved word class, private, public and
all the necessary data and functions does not create an object. All the information
necessary to construct a new object is now available. It remains for the definition
of an object to breathe life into your new creation. This creation or construction

Chapter XXVI Focus on OOP, Encapsulation 26.30


process is not very difficult and uses the same time honored syntax of data type
followed by variable identifier. In the previous program example this was done in
the main function.

Defining and Using a New Object

A class is a data type that is used to construct a new variable


object. The definition of an object uses the following syntax:

class-identifier object-identifier;

In the example below Jeep is defined as a new object


of the class Car.

void main()
{
clrscr();
Car Jeep;
Jeep.GetData();
Jeep.ShowData();
}

The statement Car Jeep; is called the instantiation of the


new object Jeep. This is the precise instance that the new
object is created. It is the moment that the constructor
of the new Jeep object is automatically called.

After the instantiation of Jeep, its public functions can be


accessed by using the object identifier, followed by a period,
and the member function identifier.

Two examples of calling member functions are shown in the


main function example, above.

I have mentioned before that OOP has its share of vocabulary. Whether all this
vocabulary is good or bad is a dead issue. The vocabulary exists and it gets used
in the computer science community. Students who are not familiar with the
vocabulary can be bewildered, and totally unaware that a simple concept is
discussed with a complicated word.

Chapter XXVI Focus on OOP, Encapsulation 26.31


Class Vocabulary for Data and Functions

In this book I will frequently use attributes or data members


to describe some object’s data and use actions or member
functions to describe the functions that access the
object’s data. It is possible that you will encounter any of
of the following additional words with the same meaning:

Attributes:
state
data members
member variables

Actions:
behaviors
operations
member functions
methods
services

There may be arguments by some people which words should be used. My


intention is to share a variety of vocabulary words that seem to be used in pretty
much the same manner that I use attributes and actions. Do yourself a big favor,
do not be afraid to appear a little ignorant. If somebody uses some vocabulary
that does not make sense, speak up and ask for its meaning. You will be amazed
how frequently it turns out that you are completely familiar with the concept
discussed. It just means that you are not familiar with the vocabulary.

Member functions are actions that act on the attributes of an object. There are so
many member functions that a classification has been established that indicates
the nature of the member functions and how they behave. All functions in a class
are member functions, but they do not all behave in the same way.
Member Function Categories

There are four categories of member functions:

Constructors
Member functions that create objects of a class.

Chapter XXVI Focus on OOP, Encapsulation 26.32


Destructors
Member functions that destroy created objects.

Accessors
Member functions that access the attributes of an
object without changing data values.

Modifiers
Member functions that access the attributes of
objects and alter their values.

26.7 Classes and Include Files

This chapter started by showing you how to handle a variety of include file
situations. Include files are used heavily in object oriented programming. Right
now we need to look at the manner that you will normally encounter program
source code that uses object oriented programming.

One major goal of computer science is information hiding. Perhaps this seems
like an odd goal. Why hide information? Information hiding actually is very
desirable and allows people to function within their capabilities. The complexity
of today’s technology is such that extremely few people are familiar with all the
exact details of every technology aspect. Do you know precisely how a single
pixel finds its way on the monitor with a given color, at a given location? Do you
really care how that happens?
There is also another issue at hand that ties right in with the include files we
mentioned earlier. Suppose now that you do understand all the exact details that
are used in a computer program. Do you want to be surrounded by all those
details all the time? Program development requires concentrated focus and this
focus should not be cluttered by unnecessary details. In other words, when you
write a program that makes heavy use of mathematical concepts, you will focus
totally on complicated math functions when you write them. Perhaps these
functions are written with the help of some reference material, or perhaps you use
another person who is better in math than you are.

Chapter XXVI Focus on OOP, Encapsulation 26.33


At some point you are finished with your library of math routines. You test and
debug these routines thoroughly and you are happy with the results. Fine, life
moves on, and now you wish to use this bunch of math routines in the rest of the
program. It is precisely at this stage that the implementation of all these math
functions are no longer your concern. Your focus has switched. You now wish to
hide all this math business so that you can focus elsewhere without distractions.

This information hiding business can be done very nicely with object oriented
programming. Using a class requires that you are familiar with the behavior of an
object. What does the object do? What is the interface to the object? A well
written class will not allow any access to the attributes of the class. This means
that your concern is with the actions of the class that control what goes and does
not go. Using member functions requires an understanding of the function
preconditions, postconditions and parameters. Knowing the complete function
implementation is not necessary.

So what does all of this mean when you work with a class? It means that our
driving program should include the header file. This header file normally uses the
suffix *.H and provides the interface to a class. The header file contains the class
declaration and usually any required information that is necessary to use the class,
such as the preconditions and postconditions of member functions.

The function implementations are placed elsewhere in another file. Frequently,


information hiding goes to the extent that the implementation file is a compiled
object file that is not only hidden from view in spirit, but also in physical reality.
You do not know how the functions are implemented and you have no way of
finding this out.

Program PROG2607.CPP is a short program that demonstrates the use of a Car


class. The main driving program includes the header file, CAR.H, and the
implementation of the member functions are placed in the CAR.CPP file. This
program uses the same Car class that has been used before in this chapter.

// PROG2607.CPP
// This program demonstrates the conventional approach
of
// including the header file, CAR.H, which contains the
class
// declaration. The member functions are in CAR.CPP.

#include <iostream.h>
#include <conio.h>
#include "CAR.H" // the interface header file of
Car

Chapter XXVI Focus on OOP, Encapsulation 26.34


void main()
{
clrscr();
Car Jeep;
Jeep.GetData();
Jeep.ShowData();
}

The main program provides little information. You can see that Jeep is
instantiated as an object of the Car class and then two member functions are
called. It is not possible to get much understanding from this file about the Car
class. We will need to go elsewhere to get better information. Take a look at the
CAR.H file, which should provide the information to use the Car class properly.

// CAR.H
// This is the header file included by PROG2607.CPP
// that provides the interface to the Car class.

#ifndef _CAR_H
#define _CAR_H

class Car
{
private:
int Year; // stores year that the car was
built
double Value; // stores the resale value of
the car
public:
Car(); // constructor initializes
private data
~Car(); // destructor
void GetData(); // places entered values in
private data
void ShowData(); // displays private data values
};

#include "CAR.CPP"

#endif

Chapter XXVI Focus on OOP, Encapsulation 26.35


The CAR.H file is not very exotic, but provides information about the Car class.
You can see the public member functions, and what actions they perform. Note
also that the header file uses the #ifndef preprocessor to prevent duplicate
inclusion of the same file. And at the end of the header file the CAR.CPP file of
member function implementations is included.

You will not always see the approach of including the implementation file at the
end of the header file. Frequently, the implementation file is a compiled object
file that is included in a special program project. I mentioned earlier that this
topic varies wildly across different C++ version and the demonstrated approach
works nicely with every compiler that you may use.

// CAR.CPP
// This file contains the functions implementations of
the Car
// class declared in CAR.H and used by program
PROG2607.CPP.

Car::Car()
{
cout << endl << endl;
cout << "CAR IS CREATED" << endl;
Year = 0;
Value = 0;
}

Car::~Car()
{
cout << endl << endl;
cout << "CAR IS FINISHED" << endl;
getch();
}

void Car::GetData()
{
cout << endl << endl;
cout << "Enter car year ===>> ";
cin >> Year;
cout << "Enter car value ===>> ";
cin >> Value;
}

void Car::ShowData()
{
cout << endl << endl;

Chapter XXVI Focus on OOP, Encapsulation 26.36


cout << "CAR YEAR: " << Year << endl;
cout << "CAR VALUE: " << Value << endl;
}

PROG2607.CPP OUTPUT

CAR IS CREATED

Enter car year ===>> 1953


Enter car value ===>> 1700

CAR YEAR: 1953


CAR VALUE: 1700

CAR IS FINISHED

Header File Conventions

Declare a class in a special, include, header file.

Traditionally, the header file uses a *.H file suffix.

Provide necessary comments, such as preconditions,


postconditions and function descriptions to help explain
the public member functions.

Include the implementation file of the member functions at


the end of the header file or create a project that organizes
the necessary files for the compiling, linking and executing

Chapter XXVI Focus on OOP, Encapsulation 26.37


of a program.

Creating a project in C++ is very compiler and version


dependent. Consult your C++ reference manual for
specific details.

The previous example involved one class. It is entirely possible that some library
involves a variety of classes with some common purpose. In such a case your
header file may contain the declarations of more than one class. At the same time
the implementation file will include details for member functions of more than
one class. Program PROG2608.CPP shows how to handle a header file and an
implementation file for such a situation.
// PROG2608.CPP
// This program creates two classes with a member
function
// to display the value of the Data field.

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

void main()
{
clrscr();
One ObjectOne;
Two ObjectTwo;
ObjectOne.ShowOne();
ObjectTwo.ShowTwo();
getch();
}

// ONETWO.H
// This file is the header file included in
PROG2608.CPP

class One
{
private:
int DataOne;
public:
One(); // constructor initializes
DataOne to 1
void ShowOne(); // member function that displays

Chapter XXVI Focus on OOP, Encapsulation 26.38


DataOne
};

class Two
{
private:
int DataTwo;
public:
Two(); // constructor initializes
DataTwo to 2
void ShowTwo(); // member function that displays
DataTwo
};

#include "ONETWO.CPP"

// ONETWO.CPP
// This is the implementation file of classes One and
Two

One::One()
{
DataOne = 1;
}

Two::Two()
{
DataTwo = 2;
}

void One::ShowOne()
{
cout << endl;
cout << "ShowOne Data: " << DataOne << endl;
}

void Two::ShowTwo()
{
cout << endl;
cout << "ShowTwo Data: " << DataTwo << endl;
}

Chapter XXVI Focus on OOP, Encapsulation 26.39


PROG2608.CPP OUTPUT

ShowOne Data: 1

ShowTwo Data: 2

26.8 Understanding the “this” Pointer

You are pleased to see a section on understanding the this pointer. Your pleasure
no doubt is increased by the fact that you have never even heard such a pointer
mentioned, or forgotten that it was mentioned, and pray tell what does this weird
pointer have to do with object oriented programming? Well hang on because you
are about to understand something mysterious that has been going on.

Since the first introduction of objects you have been shown that the compiler
knows where to find member data. Most students do not find this all that strange,
because you are confident the syntax of providing a class identifier with a scope
resolution operator and function identifier solves the problem. A function like:

void Worker :: ShowData()


{
cout << Name << endl;
cout << Age << endl;
}

does a good job communication that ShowData is a member function of the


Worker class. The result is that Name and Age can be accessed without any
difficulties. You have probably accepted this reality without concern, and now
you are about to learn why this manages to work like it does.

The secret is accomplished by a special pointer that keeps track of the object
currently being used. Program PROG2609.CPP uses a simple Animal class to
demonstrate what the this pointer is about. Different features of the program will
be explained inside the program with comments.

Chapter XXVI Focus on OOP, Encapsulation 26.40


// PROG2609.CPP
// This program demonstrates how a member function
identifies
// the memory location of its data field with the
implied "this"
// pointer. It also shows two ways to dereference a
pointer.

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

class Animal
{
private:
int Data1;
float Data2;
public:
Animal(int,float);
void ShowData();
};

// The Animal class has data members, Data1 and Data2, to store an integer and a
real number.
// The class has a constructor and a ShowData member function.

// The main function instantiates two objects, Aardvark and Wallaby.


// The constructor initializes the Data1 and Data2 data members.

void main()
{
clrscr();
Animal Aardvark(1111,3.14);
Animal Wallaby(2222,6.28);
cout << "Aardvark address: " << &Aardvark << endl;
Aardvark.ShowData();
cout << endl;
cout << "Wallaby address: " << &Wallaby << endl;
Wallaby.ShowData();
getch();
}

// The main function displays the memory addresses of the Aardvark and Wallaby
objects.

Chapter XXVI Focus on OOP, Encapsulation 26.41


// Compare these addresses with the information displayed by the ShowData
function.

Animal::Animal(int D1, float D2)


{
Data1 = D1;
Data2 = D2;
}

void Animal::ShowData()
{
cout << "this address: " << this << endl;
cout << "Data1: " << Data1 << endl;
cout << "Data2: " << Data2 << endl;
cout << "(*this).Data1: " << (*this).Data1 <<
endl;
cout << "this->Data2: " << this->Data2 <<
endl;
}

// The ShowData function displays some pretty weird stuff. This function will be
// explained in detail after the program output display. You will see that various
// addresses are displayed. Check the output to see how these values compare
with
// the output provided by the main function.

An explanation of the bizarre ShowData function will need to wait until after you
have studied the program output. In particular, note that the memory address of
the Aardvark object and the elusive this pointer are the same. At this stage, look
at that, absorb the fact that there is some relationship between the new object and
some odd, special reserved identifier, called this.

PROG2609.CPP OUTPUT

Aardvark address: 0x8f42fff0


this address: 0x8f42fff0
Data1: 1111

Chapter XXVI Focus on OOP, Encapsulation 26.42


Data2: 3.14
(*this).Data1: 1111
this->Data2: 3.14

Wallaby address: 0x8f42ffea


this address: 0x8f42ffea
Data1: 2222
Data2: 6.28
(*this).Data1: 2222
this->Data2: 6.28

The ShowData function needs to be placed under a microscope and examined


very closely. There are many new, and important, concepts revealed in that
modest function.

void Animal::ShowData()
{
cout << "this address: " << this << endl;
cout << "Data1: " << Data1 << endl;
cout << "Data2: " << Data2 << endl;
cout << "(*this).Data1: " << (*this).Data1 <<
endl;
cout << "this->Data2: " << this->Data2 <<
endl;
}

Let us start at the first weird statement, which outputs the value of the this
identifier. Observe that there is not an ampersand & in front of the identifier, yet
the output is a memory address. Displaying a memory address without
specifically asking for memory with the & operator indicates that the identifier
must be a pointer. Remember that a pointer stores the location of a memory
address.

It is also very significant that the this pointer displays the same memory address
as the address of the Aardvark object in the main function. The same is true of
the Wallaby object. This pretty much indicates a very strong connection between
Aardvark and this, as well as Wallaby and this.

The next two statements display the values of Data1 and Data2 in a straight
forward manner. No new concepts are shown here. The output of the data will be
compared shortly.

Chapter XXVI Focus on OOP, Encapsulation 26.43


In an earlier chapter about pointers you were told that the contents of a memory
address can be viewed with the asterisk * operator. The process of accessing the
contents of a memory location is called dereferencing.

cout << "(*this).Data1: " << (*this).Data1 << endl;

Now the program statement with (*this).Data1 is doing some kind of


dereferencing. We have established that this is a pointer. We have also
established that the this pointer stores the same memory address as the Aardvark
object. The contents of the Aardvark object are the various fields of the Animal
class like Data1 and Data2.

Now with the * operator we can dereference the this pointer and view its
contents. But it is not that simple. Memory locations are not just addresses but
addresses of certain data types and this points to a class with various fields.
Displaying the contents of a class, like a struct, can only be done by specifying
individual fields. So in the statement above we output the contents located at the
memory of the this pointer, but only the Data1 field.
It appears that this gets around, because you see the same relationship with the
this pointer and the Wallaby object. The address of the object that is currently
accessed has the same address as the this pointer.

The parenthesis around (*this) are necessary because of precedence problems. If


you remove the parenthesis you will find that the program does not compile. The
period operator has higher precedence than the asterisk operator and the result is
that an attempt is made to dereference Data1. You may think this is precisely the
intention, but dereferencing only is possible with a pointer. Data1 is not a
pointer. It is an integer field in the Animal class. The parenthesis steers the
computer to the memory location of this and dereferences that memory location.
Then the computer continues and focuses on the Data1 field.

Well C++ just had to make your life a little more complicated by providing a
second method of dereferencing. You can also use the -> operator after a
pointer identifier to indicate that the pointer is dereferenced and follow it with the
specific field identifier whose value is accessed.

cout << "this->Data2: " << this->Data2 << endl;

It is quite possible that you understood every statement on a line by line basis, but
you are not seeing the big picture. Inside a member function you can access

Chapter XXVI Focus on OOP, Encapsulation 26.44


object attributes directly with statements like cout << Data1; You can also
achieve the exact same result by using cout << (*this).Data1;

Now comes the secret. The this pointer is used everywhere. You do not see it,
but this is there with any object access. You can actually include the this pointer
in a statement like I showed you. However, if this is left out, the compiler will
assume its presence for you and puts it there automatically.

Member Functions and the this pointer

When a new object is instantiated, an invisible pointer, called


the this pointer, stores the memory location of the new object.

Accessing members of the new object is handled by the


this pointer. The presence of the this pointer is implied,
even if you do not include this in any program statement.

You can use the this pointer and dereference any contents
of its memory address with two methods. These two methods
are shown with two program statements that both have
the same exact meaning.

cout << (*this).Data1 << endl;

cout << this->Data1 << endl;

APCS Examination Alert

It is proper to use (*this).Data1 or this->Data1 when

Chapter XXVI Focus on OOP, Encapsulation 26.45


you dereference a pointer. However, it is likely that you will
only see the this->Data style used with APCS
Examination questions.

26.9 List Class Case Study

It is time for another small case study. In this case study you will investigate the
process of creating a List class as the class is developed through five stages. The
majority of steps shown in this case study do not present new information. It is
meant as a review and reinforcement of the principles discussed so far in this
chapter. The final stage will demonstrate how to use a member function that
requires information to be passed by parameter.

The List Class, Step 1

The first step of the List class is to set the stage with minimal syntax that
compiles. This program does compile, and it does run, even if there are neither
attributes nor actions in the List class declaration.

// PROG2610
// First stage of the List class
// The minimal program that does compile.

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

class List
{
private:
public:
};

Chapter XXVI Focus on OOP, Encapsulation 26.46


void main()
{
clrscr();
cout << "LIST CLASS STAGE 1" << endl;
List L;
getch();
}

PROG2610.CPP OUTPUT

LIST CLASS STAGE 1

The List Class, Step 2


The second stage adds the List attributes, which is an integer array and a simple
data type that stores the size of the array. The array can hold up to 100 integers.
In this stage there is also a constructor member function added. The constructor
initializes every element of the array to zero.
// PROG2611.CPP
// Second stage of the List class
// This stage puts in the private data and a
constructor to
// initialize the data values.

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

class List
{
private:
int Array[100];
int Size;
public:
List();
};

void main()
{
clrscr();
cout << "LIST CLASS STAGE 2" << endl;
List L;

Chapter XXVI Focus on OOP, Encapsulation 26.47


getch();
}

List::List()
{
Size = 100;
int K;
for (K=0; K<Size; K++)
Array[K] = 0;
cout << endl;
cout << "List object is constructed" << endl;
}

PROG2611.CPP OUTPUT

LIST CLASS STAGE 2

List object is constructed

The List Class, Step 3


Stage 3 adds three member function prototypes to the List class declaration, and
creates stubs for the implementation of each member function. The stubs do
include an output statement indicating that the function has been called.

// PROG2612.CPP
// Third stage of the List class
// This stage adds the RandomList, DisplayList and
SortList
// member functions stubs.

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

class List
{
private:

Chapter XXVI Focus on OOP, Encapsulation 26.48


int Array[1000];
int Size;
public:
List();
void RandomList();
void DisplayList();
void SortList();
};

void main()
{
clrscr();
cout << "LIST CLASS STAGE 3" << endl;
List L;
L.RandomList();
L.SortList();
L.DisplayList();
getch();
}

List::List()
{
cout << endl << endl;
Size = 1000;
int K;
for (K=0; K<Size; K++)
Array[K] = 0;
cout << "LIST OBJECT IS CONSTRUCTED" << endl;
}

void List::RandomList()
{
cout << endl << endl;
cout << "RANDOM LIST FUNCTION" << endl;
}

void List::DisplayList()
{
cout << endl << endl;
cout << "DISPLAY LIST FUNCTION" << endl;
}

void List::SortList()
{
cout << endl << endl;
cout << "SORT LIST FUNCTION" << endl;
}

Chapter XXVI Focus on OOP, Encapsulation 26.49


PROG2612.CPP OUTPUT

LIST CLASS STAGE 3

LIST OBJECT IS CONSTRUCTED

RANDOM LIST FUNCTION

SORT LIST FUNCTION

DISPLAY LIST FUNCTION

The List Class, Step 4


In the fourth stage, the implementation of the RandomList, DisplayList and
SortList member functions are finished, and the program is ready to run in a
meaningful manner.

// PROG2613.CPP
// Fourth stage of the List class
// This stage implements the member functions
// RandomList, DisplayList and SortList.

#include <iostream.h>
#include <conio.h>
#include <stdlib.h>
#include "BOOL.H"

class List
{
private:
int Array[1000];
int Size;
public:
List();
void RandomList();
void DisplayList();
void SortList();
};

Chapter XXVI Focus on OOP, Encapsulation 26.50


void main()
{
clrscr();
cout << "LIST CLASS STAGE 4" << endl;
List L;
L.RandomList();
L.SortList();
L.DisplayList();
getch();
}
List::List()
{
cout << endl << endl;
Size = 1000;
int K;
for (K=0; K<Size; K++)
Array[K] = 0;
cout << "List object is constructed" << endl;
}

void List::RandomList()
{
cout << endl << endl;
cout << "RANDOM LIST FUNCTION" << endl;
cout << endl;
cout << "Enter size of the list [1..1000] ===>> ";
cin >> Size;
int K;
for (K=0; K<Size; K++)
Array[K] = random(9000) + 1000; // [1000..9999]
range
}

void List::DisplayList()
{
cout << endl << endl;
cout << "DISPLAY LIST FUNCTION" << endl;
cout << endl;
int K;
for (K=0; K<Size; K++)
{
cout << Array[K] << " ";
if (K%10 == 9)
cout << endl;
}
}

Chapter XXVI Focus on OOP, Encapsulation 26.51


void List::SortList()
{
cout << endl << endl;
cout << "SORT LIST FUNCTION" << endl;
int P,Q,T;
for (P=1; P<Size-1; P++)
for (Q=0; Q<Size-P; Q++)
if (Array[Q] > Array[Q+1])
{
T = Array[Q];
Array[Q] = Array[Q+1];
Array[Q+1] = T;
}
}

PROG2614.CPP OUTPUT

LIST CLASS STAGE 4

List object is constructed

RANDOM LIST FUNCTION


Enter size of the list [1..1000] ===>> 40

SORT LIST FUNCTION

DISPLAY FUNCTION

1035 1095 1299 1373 1980 2025 2092 2686 2761


2954
2974 3473 4201 4016 4655 4841 4998 5059 5273
5419
5832 5942 6078 6397 6442 6954 7041 7169 7283
7302
7480 7879 8297 8339 8448 8555 9308 9549 9573
9635

The List Class, Step 5

Chapter XXVI Focus on OOP, Encapsulation 26.52


In the final stage of the List Class case study, one more member function is
added. This new function, Search, is different from the previous functions
because it received information passed by parameter. All member functions,
which you have seen thus far, have manipulated the variables of the class and
parameters were not necessary, courtesy of the nifty this pointer. Now you will
see that parameters are alive and well, and very important with objects also. This
stage also includes a function, SearchItem, that is not a member function of List.
Function SearchItem is used to test the new Search member function of the List
class.

Function Search is a binary search function with the following prototype:

int Search(int Item);

The int parameter provides the number that the Search function is expected to
find. If the number is found, the location (index) of the number in the array is
returned, otherwise a value of -1 is returned.

The complete program is shown. The majority of the code you have seen before,
but the few extra pages required to duplicate the code is meant to make studying
the case study easier by seeing the context of the new stage.

// PROG2614.CPP
// Fifth stage of the List class case study
// This stage adds the Search member function.
// Search introduces a member function with a
parameter.

#include <iostream.h>
#include <conio.h>
#include <stdlib.h>
#include "BOOL.H"
class List
{
private:
int Array[1000];
int Size;
public:
List();
void RandomList();
void DisplayList();
void SortList();
int Search(int Item);
};

Chapter XXVI Focus on OOP, Encapsulation 26.53


void SearchItem(List);

void main()
{
clrscr();
cout << "LIST CLASS STAGE 5" << endl;
List L;
L.RandomList();
L.SortList();
L.DisplayList();
SearchItem(L);
getch();
}

void SearchItem(List L)
{
cout << endl << endl;
cout << "FUNCTION SEARCH ITEM" << endl;
cout << endl;
int Item;
int Index;
cout << "Enter search item ===>> ";
cin >> Item;
cout << endl;
Index = L.Search(Item);
if (Index == -1)
cout << Item << " was not found in the list " <<
endl;
else
cout << Item << " is located at index " << Index
<< endl;

int List::Search(int Item)


{
bool Found = false;
int Small = 0;
int Large = Size-1;
int Middle;
while (Small < Large+1 && !Found)
{
Middle = (Small + Large) / 2;
if (Array[Middle] == Item)
Found = true;
else
if (Array[Middle] > Item)
Large = Middle-1;
else
Small = Middle+1;

Chapter XXVI Focus on OOP, Encapsulation 26.54


}
if (Found)
return Middle;
else
return -1;
}

List::List()
{
cout << endl << endl;
Size = 1000;
int K;
for (K=0; K<Size; K++)
Array[K] = 0;
cout << "List object is constructed" << endl;
}

void List::RandomList()
{
cout << endl << endl;
cout << "RANDOM LIST FUNCTION" << endl;
cout << endl;
cout << "Enter size of the list [1..1000] ===>> ";
cin >> Size;
int K;
for (K=0; K<Size; K++)
Array[K] = random(9000) + 1000; // [1000..9999]
range
}

void List::DisplayList()
{
cout << endl << endl;
cout << "DISPLAY LIST FUNCTION" << endl;
cout << endl;
int K;
for (K=0; K<Size; K++)
{
cout << Array[K] << " ";
if (K%10 == 9)
cout << endl;
}
}

void List::SortList()
{
cout << endl << endl;

Chapter XXVI Focus on OOP, Encapsulation 26.55


cout << "SORT LIST FUNCTION" << endl;
int P,Q,T;
for (P=1; P<Size-1; P++)
for (Q=0; Q<Size-P; Q++)
if (Array[Q] > Array[Q+1])
{
T = Array[Q];
Array[Q] = Array[Q+1];
Array[Q+1] = T;
}
}

PROG2614.CPP OUTPUT

LIST CLASS STAGE 5

List object is constructed

RANDOM LIST FUNCTION

Enter size of the list [1..1000] ===>> 40

SORT LIST FUNCTION

DISPLAY FUNCTION

1035 1095 1299 1373 1980 2025 2092 2686 2761


2954
2974 3473 4201 4016 4655 4841 4998 5059 5273
5419
5832 5942 6078 6397 6442 6954 7041 7169 7283
7302
7480 7879 8297 8339 8448 8555 9308 9549 9573
9635

FUNCTION SEARCH ITEM

Enter search item ===>> 8555

Chapter XXVI Focus on OOP, Encapsulation 26.56


8555 is located at index 35

26.10 Constant Member Functions

The const reserved word has been used several times. It was first introduced as a
means to assign a value to an identifier that cannot be altered. The term const is
literal and means constant value. You have also seen const used with parameter
passing. It is possible to make a reference parameter const. The benefit of this
feature is to allow passing by reference of some large data structure that saves
both copy time and memory space. Yet at the same time const prevents altering
the parameter. It was shown to be a special way of passing parameters that
combined the advantages of both value and reference parameters.

Our hardworking const is not finished yet, and this time you will see how const
also can be used with the member functions of a class. If you suspect that once
again something will not change, you are correct.
However, do wait for one brief program. The const issue is the purpose of this
section. First a program will be shown that demonstrates how very short, one-
line, member functions are frequently used in a class declaration. In the case of
using a short function, it is quite common that you do not use a function
prototype, followed by the complete implementation coded elsewhere. Short
functions can quite conveniently be completed inside the class declaration without
causing any clutter and confusion. It is true that this approach may violate the
idea of information hiding, but my goal is to present different approaches right
now and not philosophy about what is best. These same short member functions
will then be used in the next program example with const.

// PROG2615.CPP
// This program demonstrates the practice of placing
complete
// functions inside a class declaration when such
functions
// are very small.

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

Chapter XXVI Focus on OOP, Encapsulation 26.57


class CardDeck
{
private:
int NumDecks;
int NumPlayers;
int CardsLeft;
public:
CardDeck();
int GetDecks() { return NumDecks; }
int GetPlayers() { return NumPlayers; }
int GetCards() { return CardsLeft; }
};

void main()
{
clrscr();
CardDeck D;
cout << "Number of decks: " << D.GetDecks() <<
endl;
cout << "Number of players: " << D.GetPlayers() <<
endl;
cout << "Number of cards: " << D.GetCards() <<
endl;
getch();
}

CardDeck::CardDeck()
{
NumDecks = 2;
NumPlayers = 4;
CardsLeft = NumDecks * 52;
}

PROG2615.CPP OUTPUT

Number of decks: 2
Number of players: 4
Number of cards: 104

Chapter XXVI Focus on OOP, Encapsulation 26.58


With program PROG2616.CPP the previous program is repeated. The only
difference is that each member function of the CardDeck class is preceded by
const. Function GetDecks is intentionally written twice. The first time the
function returns the value of the NumDecks variable. The second GetDecks
function is commented out, but note that it attempts to increment the value of
NumDecks before returning that value.

When you run the program the first time, everything compiles and works fine.
Each member function is a classic example of an accessor member function
which gets values without trying to modify any data. The const provides extra
protection for functions that should not be altering data. Now change the
comments and try to compile the program with the first GetDecks commented
out. Uncomment the second CardDeck, and now the program will not compile.
The result is shown in the second program output below.

// PROG2616.CPP
// This program demonstrates the common practice of
making
// member functions "constant" functions to avoid
altering
// any private data.

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

class CardDeck
{
private:
int NumDecks;
int NumPlayers;
int CardsLeft;
public:
CardDeck();
int GetDecks() const { return NumDecks; }
// int GetDecks() const { NumDecks++; return
NumDecks; }
int GetPlayers() const { return NumPlayers; }
int GetCards() const { return CardsLeft; }
};

Chapter XXVI Focus on OOP, Encapsulation 26.59


void main()
{
clrscr();
CardDeck D;
cout << "Number of decks: " << D.GetDecks() <<
endl;
cout << "Number of players: " << D.GetPlayers() <<
endl;
cout << "Number of cards: " << D.GetCards() <<
endl;
getch();
}

CardDeck::CardDeck()
{
NumDecks = 2;
NumPlayers = 4;
CardsLeft = NumDecks * 52;
}

PROG2616.CPP OUTPUT #1

Number of decks: 2
Number of players: 4
Number of cards: 104

PROG2616.CPP OUTPUT #2

The program has no program execution, and generates the following error
message:

Compiling PROG2515.CPP:
Error PROG2616.CPP 19: Cannot modify a const object

const Member Functions

Chapter XXVI Focus on OOP, Encapsulation 26.60


When you declare a class member function const, you
are indicating that the member function will not modify
any of the class attributes.

Sample const member functions:

int GetDecks() const; // prototype


int GetDecks() const { return NumDecks; }

All accessor functions should be declared as const


member functions.

Chapter XXVI Focus on OOP, Encapsulation 26.61

You might also like