Encapsulation of Objects C++ PDF
Encapsulation of Objects C++ PDF
A. Goodman
Deakin University
Module 818
Aim
After working through this module you should be able to create and use
objects in C++, incorporating encapsulation and information hiding
Learning objectives
After working through this module you should be able to:
1. Identify the advantages of using encapsulated objects in C++
2. List the advantages of using information hiding in C++
3. Describe the process of object-oriented messaging in C++
4. Define and use constructors and destructors in C++
5. List the correct methods for using classes for object packaging in
C++
6. Define and use abstract data types in C++
7. Use objects within C++ programs
8. List the advantages of using operator overloading in C++
Content
Definition of objects in C++
Encapsulation of data and methods
Constructors and destructors
Abstract data types
Operator overloading
Learning Strategy
Read the printed module and the assigned readings and complete the
exercises as requested.
Assessment
Completion of exercises and the CML test at the end of the module.
Page 818-1
Module 818
Objective 1
After working through this module you should be able to identify the
advantages of using encapsulated objects in C++
Encapsulation is the basic process of forming objects. An encapsulated
object is often called an abstract data type. Without encapsulation,
which involves the use of one or more classes, there is really no basis
for object-oriented programming.
Exercise 1
Describe in simple terms the process of encapsulation.
Page 818-2
Module 818
Objective 2
After working through this section you should be able to list the
advantages of using information hiding in C++
The next program [CLAS.CPP] has limited information hiding. This
program is identical to the last one except for the way it does some of
its operations. We will take the differences one at a time and explain
what is happening. Keep in mind that this is a trivial program and the
safeguards built into it are not needed for such a simple program but are
used here to illustrate how to use these techniques in a larger and more
complicated program.
#include <iostream.h>
class one_datum {
int data_store;
public:
void set(int in_value);
int get_value(void);
};
void one_datum::set(int in_value)
{
data_store = in_value;
}
int one_datum::get_value(void)
{
return data_store;
}
void main()
{
one_datum dog1, dog2, dog3;
int piggy;
dog1.set(12);
dog2.set(17);
dog3.set(-13);
piggy = 123;
// dog1.data_store = 115;
This is illegal in C++
// dog2.data_store = 211;
This is illegal in C++
cout << "The value of dog1 is " << dog1.get_value() << "\n";
cout << "The value of dog2 is " << dog2.get_value() << "\n";
cout << "The value of dog3 is " << dog3.get_value() << "\n";
cout << "The value of piggy is " << piggy << "\n";
}
Module 818
Page 818-4
Module 818
Class
Object
Method
Message
Exercise 2
Define the difference between a class and an object.
Modify CLAS.CPP by adding a method to calculate the square of the
stored value. Amend the main program to read and display the resulting
squared values.
Add a constructor to CLAS.CPP to initialise the stored value to 10, and
amend the main program to display the values immediately after the
object definition.
Page 818-5
Module 818
Objective 3
Problems
The next program [OPENPOLE.CPP] has a few serious problems that
will be overcome in the next example program by using the principles of
encapsulation.
Page 818-6
Module 818
We have two structures declared, one a rectangle and the other a pole.
The data fields should be self-explanatory with the exception of the
depth of the flagpole, which is the depth it is buried in the ground. The
overall length of the pole is therefore the sum of the length and the
depth.
Based on your experience with ANSI-C, you should have no problem
understanding exactly what this program is doing, but you may be a bit
confused at the meaning of the result found in lines 28 and 29, where
we multiply the height of the square with the width of the box. This is
perfectly legal in ANSI-C or C++, but the result has no meaning
because the data are for two different entities. Likewise, the result
calculated in lines 30 and 31 is even sillier because the product of the
height of the square and the depth of the flagpole has absolutely no
meaning in any real-world physical system we can think up.
Would it not be convenient if we had some way to prevent such things
from happening in a large production program? If we had a program
that defined all of the things we can do with a square and another
program that defined everything we could do with a pole, and if the
data could be kept mutually exclusive, we could prevent these things
from happening. The next program will do just those things for us and
do it in a very elegant way.
Page 818-7
Module 818
In this program the rectangle is changed to a class with the same two
variables (which are now private) and two methods to handle the
private data. One method is used to initialise the values of the objects
created and the other method to return the area of the object. The two
methods are defined in lines 7 to 17 in the manner described previously.
The pole is left as a structure to illustrate that the two can be used
together and that C++ is truly an extension of ANSI-C.
In line 24 we declare two objects, once again named box and square,
but this time we cannot assign values directly to their individual
components because they are private elements of the class. Lines 26 to
28 are commented out for that reason and the messages are sent to the
objects in lines 29 and 30 to tell them to initialise themselves to the
values input as parameters. The flag_pole is initialised in the same
manner as in the previous program. Using the class in this way prevents
us from making the silly calculations we did in the last program. The
Page 818-8
Module 818
Module 818
Cost of encapsulation
It should be clear that this technique has an efficiency cost because
every access of the elements of the object will require a call to a
method. In building a large program, however, this could easily be
saved in debug time. This is because a program made up of objects that
closely match the application are much easier to understand than a
program that does not.
In a real project, however, there could be considerable savings in
development time (and therefore cost) if someone developed all the
details of the rectangle, programmed them, and made them available to
you to use.
This is exactly what has been done if you regard the video monitor
(screen) as an object. There is a complete set of pre-programmed and
debugged routines you can use to make the monitor do anything you
wish it to do, all you have to do is study the interface to the routines
and use them, expecting them to work. It is impossible for you to
multiply the size of your monitor screen by the depth of the flag pole
because that information is not available to you to use in a corruptible
or meaningless way.
Exercise 3
Describe how a message is sent to a method.
Page 818-10
Module 818
Objective 4
After working through this section you should be able to define and use
constructors and destructors in C++
The next program [CONSPOLE.CPP] introduces constructors and
destructors.
#include <iostream.h>
class rectangle {
// A simple class
int height;
int width;
public:
rectangle(void);
// with a constructor,
int area(void);
// two methods,
void initialize(int, int);
~rectangle(void);
// and a destructor
};
rectangle::rectangle(void)
// constructor
{
height = 6;
width = 6;
}
int rectangle::area(void)
// Area of a rectangle
{
return height * width;
}
void rectangle::initialize(int init_height, int init_width)
{
height = init_height;
width = init_width;
}
rectangle::~rectangle(void)
// destructor
{
height = 0;
width = 0;
}
struct pole {
int length;
int depth;
};
void main()
{
rectangle box, square;
pole flag_pole;
cout << "The area of the box is " << box.area() << "\n";
cout << "The area of the square is " << square.area() << "\n";
// box.height = 12;
// box.width = 10;
// square.height = square.width = 8;
box.initialize(12, 10);
square.initialize(8, 8);
flag_pole.length = 50;
flag_pole.depth = 6;
cout << "The area of the box is " << box.area() << "\n";
cout << "The area of the square is " << square.area() << "\n";
// cout << "The funny area is " <<
//
area(square.height, box.width) << "\n";
// cout << "The bad area is " <<
//
area(square.height, flag_pole.depth) << "\n";
}
This program is identical to the last except that a constructor has been
added as well as a destructor. The constructor always has the same
name as the class itself and is declared in line 8 then defined in lines 11
to 15. The constructor is called automatically by the C++ system when
Page 818-11
Module 818
Exercise 4
Define the terms constructor and destructor.
Page 818-12
Module 818
Page 818-13
Module 818
Objective 5
After working through this section you should be able to list the correct
methods for using classes for object packaging in C++
The program below [BOXES1.CPP] is an example of how not to
package an object for universal use. This packaging is actually fine for a
very small program but is meant to illustrate to you how to split your
program up into smaller more manageable files when you are
developing a large program or when you are part of a team developing
a large system. The next three example programs in this section will
illustrate the proper method of packaging a class.
#include <iostream.h>
class box {
int length;
int width;
public:
box(void);
//Constructor
void set(int new_length, int new_width);
int get_area(void) {return (length * width);}
~box(void);
//Destructor
};
box::box(void)
//Constructor implementation
{
length = 8;
width = 8;
}
// This method will set a box size to the two input parameters
void box::set(int new_length, int new_width)
{
length = new_length;
width = new_width;
}
box::~box(void)
//Destructor
{
length = 0;
width = 0;
}
void main()
{
box small, medium, large;
//Three boxes to work with
small.set(5, 7);
// Note that the medium box uses the values supplied
// by the constructor
large.set(15, 20);
cout << "The small box area is " << small.get_area() << "\n";
cout << "The medium box area is " << medium.get_area() << "\n";
cout << "The large box area is " << large.get_area() << "\n";
}
This program is very similar to the last one with the pole structure
dropped and the class renamed box. The class is defined in lines 2 to 10,
the implementation of the class is given in lines 11 to 26, and the use of
the class is given in lines 29 to 36. With the explanation we gave of the
last program, you should have no problem understanding this program
in detail.
Page 818-14
Module 818
Page 818-15
Module 818
The BOX.H file is included here, in line 2, since the definition of the
box class is needed to declare three objects and use their methods. You
Page 818-16
Module 818
Page 818-17
Module 818
Exercise 5
Explain the distinction between method declaration and implementation,
and how these are managed in C++.
Page 818-18
Module 818
Objective 6
Friend functions
A function outside of a class can be defined to be a friend function by
the class which gives the friend free access to the private members of
the class. This in effect, opens a small hole in the protective shield of
the class, so it should be used very carefully and sparingly. There are
cases where it helps to make a program much more understandable and
allows controlled access to the data. Friend functions will be illustrated
in some of the example programs later in this tutorial. It is mentioned
here for completeness of this section. A single isolated function can be
declared as a friend, as well as members of other classes, and even
entire classes can be given friend status if needed in a program. Neither
a constructor nor a destructor can be a friend function.
Module 818
A Practical Class
The examples of encapsulation used so far in this Module have all been
extremely simple in order to focus attention on the mechanics of
encapsulation. In this section we will study the development of a
noticeably larger example, based upon the date class. The date class is a
complete, nontrivial class which can be used in any program to get the
current date and print it as an ASCII string in any of four predefined
formats. It can also be used to store any desired date and format it for
display. The next program file [DATE.H] is the header file for the date
class.
// This date class is intended to illustrate how to write a non// trivial class in C++. Even though this class is non-trivial,
// it is still simple enough for a new C++ programmer to follow
// all of the details.
#ifndef DATE_H
#define DATE_H
class date {
protected:
int month;
// 1 through 12
int day;
// 1 through max_days
int year;
// 1500 through 2200
static char out_string[25]; // Format output area
static char format;
// Format to use for output
// Calculate how many days are in any given month
// Note - This is a private method which can be called only
//
from within the class itself
int days_this_month(void);
public:
// Constructor - Set the date to the current date and set
//
the format to 1
date(void);
// Set the date to these input parameters
// if return = 0 ---> All data is valid
// if return = 1 ---> Something out of range
int set_date(int in_month, int in_day, int in_year);
// Get the month, day, or year of the stored date
int get_month(void)
{ return month; };
int get_day(void)
{ return day;
};
int get_year(void)
{ return year; };
// Select the desired string output format for use when the
// get_date_string is called
void set_date_format(int format_in) { format = format_in; };
// Return an ASCII-Z string depending on the stored format
//
format = 1
Aug 29, 1991
//
format = 2
8/29/91
//
format = 3
8/29/1991
//
format = 4
29 Aug 1991
Military time
//
format = ?
Anything else defaults to format 1
char *get_date_string(void);
// Return Jan Feb Mar Apr etc.
char *get_month_string(void);
};
#endif
Page 818-20
Module 818
Page 818-21
Module 818
Page 818-22
Module 818
Exercise 6
Describe how structures in C++ differ from structures in C.
Write the header file [NAME.H] for a name class, similar to the date
class. This class should store any name as three components (first name,
middle name or initial, family name) and display them in any of several
different forms (e.g. John Paul Jones; J.P.Jones; Jones, John Paul)
Page 818-23
Module 818
Objective 7
After working through this section you should be able to use objects
within C++ Programs
This section looks at how to use some of the traditional aspects of C or
C++ programming with classes and objects. Pointers to an object, as
well as pointers within an object, will be illustrated, as will arrays
embedded within an object, and arrays of objects. Since objects are
simply another C++ data construct, all of these things are possible and
can be used as needed.
We will use the BOXES1.CPP program from the last section as a
starting point and we will add a few new constructs to it for each
example program. You will recall that it was a very simple program
with the class definition, the class implementation, and the main calling
program all in one file. This was selected as a starting point because we
will eventually make changes to all parts of the program and it will be
convenient to have it all in a single file for illustrative purposes. It must
be kept in mind however that the proper way to use these constructs is
to separate them into the three files as was illustrated in BOX.H,
BOX.CPP, and BOXES2.CPP in the last section. This allows the
implementor of box to supply the user with only the interface, namely
BOX.H. Not giving her the implementation file named BOX.CPP is to
practice the technique of information hiding.
An array of objects
Examine the next program [OBJARRAY.CPP] for an example of an
array of objects. This file is nearly identical to the file named
BOX1.CPP until we come to line 33 where an array of 4 boxes is
declared.
#include <iostream.h>
class box {
int length;
int width;
static int extra_data;
// Declaration of extra_data
public:
box(void);
//Constructor
void set(int new_length, int new_width);
int get_area(void);
int get_extra(void) {return extra_data++;}
};
int box::extra_data;
// Definition of extra_data
box::box(void)
//Constructor implementation
{
length = 8;
width = 8;
extra_data = 1;
}
// This method will set a box size to the two input parameters
void box::set(int new_length, int new_width)
{
length = new_length;
width = new_width;
Page 818-24
Module 818
Recalling the operation of the constructor, you will remember that each
of the four box objects will be initialised to the values defined within the
constructor since each box will go through the constructor as they are
declared. In order to declare an array of objects, a constructor for that
object must not require any parameters. (We have not yet illustrated a
constructor with initialising parameters, but we will in the next
program.) This is an efficiency consideration since it would probably be
an error to initialise all elements of an array of objects to the same
value. We will see the results of executing the constructor when we
compile and execute the file later.
Line 36 defines a for loop that begins with 1 instead of the normal
starting index for an array leaving the first object, named group[0], to
use the default values stored when the constructor was called. You will
observe that sending a message to one of the objects uses the same
construct as is used for any object. The name of the array followed by
its index in square brackets is used to send a message to one of the
objects in the array. This is illustrated in line 36 and the operation of
that code should be clear to you. The other method is called in the
output statement in lines 39 to 41 where the area of the four boxes in
the group array are listed on the monitor.
Another fine point should be pointed out. The integer variable named
index is declared in line 36 and is still available for use in line 38 since
we have not yet left the enclosing block which begins in line 32 and
extends to line 51.
Page 818-25
Module 818
Page 818-26
Module 818
box
//Constructor
Page 818-27
Module 818
Module 818
Page 818-29
Module 818
Page 818-30
Module 818
Module 818
and is initialised to point to the object for which the member function is
invoked. This pointer is most useful when working with pointers and
especially with a linked list when you need to reference a pointer to the
object you are inserting into the list. The keyword this is available for
this purpose and can be used in any object. Actually the proper way to
refer to any variable within a list is through use of the predefined
pointer this, by writing this->variable_name, but the compiler assumes
the pointer is used, and we can simplify every reference by omitting the
pointer. The keyword this will be used in one of the larger example
programs later in this Module.
Page 818-32
Module 818
Page 818-33
Module 818
This program is very similar to the last one; in fact it is identical until
we get to the main program. You will recall that in the last program the
only way we had to set or use the embedded pointer was through use of
the two methods named point_at_next() and get_next() which are listed
in lines 33 to 39 of the present program. We will use these to build up
our linked list then traverse and print the list. Finally, we will delete the
entire list to free the space on the heap.
In lines 44 to 46 we declare three pointers for use in the program. The
pointer named start will always point to the beginning of the list, but
temp will move down through the list as we create it. The pointer
named box_pointer will be used for the creation of each object. We
execute the loop in lines 48 to 57 to generate the list where line 50
dynamically allocates a new object of the box class and line 52 fills it
with nonsense data for illustration. If this is the first element in the list,
the start pointer is set to point to this element, but if elements already
exist, the last element in the list is assigned to point to the new element.
In either case, the temp pointer is assigned to point to the last element
of the list, in preparation for adding another element if there is another
element to be added.
In line 58 the pointer named temp is pointed to the first element and it is
used to increment its way through the list by updating itself in line 62
during each pass through the loop. When temp has the value of NULL,
which it gets from the last element of the list, we are finished traversing
the list.
Finally, we delete the entire list by starting at the beginning and deleting
one element each time we pass through the loop in lines 64 to 68.
A careful study of the program will reveal that it does indeed generate a
linked list of ten elements, each element being an object of class box.
The length of this list is limited by the practicality of how large a list we
desire to print out, but it could be lengthened to many thousands of
these simple elements provided you have enough memory available to
store them all. Once again, the success of the dynamic allocation is not
checked as it should be in a well-written program.
Nesting objects
Examine the next program [NESTING.CPP] for an example of nesting
classes which results in nested objects. A nested object could be
illustrated with your computer in a rather simple manner. The computer
Page 818-34
Module 818
Page 818-35
Module 818
Exercise 7
Modify OBJDYNAM.CPP so that the objects small and medium are
pointers, and dynamically allocate them prior to their use.
Modify the loop (from line 47) in OBJLINK.CPP so that it strores up to
1000 elements in the list. (Remove the list output if necessary to speed
up the execution.) Add a test to display the amount of available memory
as the list is built, and to terminate list construction when this becomes
too small for a new list node to be added.
Page 818-36
Module 818
Objective 8
After working through this section you should be able to list the
advantages of using operator overloading in C++
The example program below [OPOVERLD.CPP] contains examples of
overloading operators. This allows you to define a class of objects and
redefine the use of the normal operators. The end result is that objects
of the new class can be used in as natural a manner as the predefined
types. In fact, they seem to be a part of the language rather than your
own add-on.
#include <iostream.h>
class box {
int length;
int width;
public:
void set(int l, int w) {length = l; width = w;}
int get_area(void) {return length * width;}
// Add two boxes
friend box operator+(box a, box b);
// Add a constant to a box
friend box operator+(int a, box b);
// Multiply a box by a constant
friend box operator*(int a, box b); };
// Add two boxes' widths together
box operator+(box a, box b)
{
box temp;
temp.length = a.length;
temp.width = a.width + b.width;
return temp;
}
box operator+(int a, box b)
// Add a constant to a box
{
box temp;
temp.length = b.length;
temp.width = a + b.width;
return temp;
}
box operator*(int a, box b)
// Multiply a box by a constant
{
box temp;
temp.length = a * b.length;
temp.width = a * b.width;
return temp;
}
void main()
{
box small, medium, large;
box temp;
small.set(2, 4);
medium.set(5, 6);
large.set(8, 10);
cout << "The area is " << small.get_area() << "\n";
cout << "The area is " << medium.get_area() << "\n";
cout << "The area is " << large.get_area() << "\n";
temp = small + medium;
cout << "The new area is " << temp.get_area() << "\n";
temp = 10 + small;
cout << "The new area is " << temp.get_area() << "\n";
temp = 4 * large;
cout << "The new area is " << temp.get_area() << "\n";
}
Page 818-37
Module 818
Page 818-38
Module 818
Page 818-39
Module 818
Page 818-40
Module 818
Separate compilation
Separate compilation is available with C++ and it follows the rules for
ANSI-C separate compilation. As expected, separately compiled files
can be linked together. However, since classes are used to define
objects, the nature of C++ separate compilation is considerably different
from that used for ANSI-C. This is because the classes used to create
the objects are not considered as external variables, but as included
classes. This makes the overall program look different from a pure
ANSI-C program. Your programs will take on a different appearance as
you gain experience in C++.
Page 818-41
Module 818
The only major difference in this class from the date class is the
overloaded constructors and methods. The program is a very practical
example that illustrates graphically that many constructor overloadings
are possible. The implementation for the time class is given below
[TIME.CPP].
#include <stdio.h>
// For the sprintf function
#include <time.h>
// For the time & localtime functions
#include "time.h"
// For the class header
char time_of_day::format;
// This defines the static data member
char time_of_day::out_string[25];
// This defines the string
// Constructor - Set time to current time and format to 1
time_of_day::time_of_day(void)
{
time_t time_date;
struct tm *current_time;
time_date = time(NULL);
current_time = localtime(&time_date);
hour = current_time->tm_hour;
minute = current_time->tm_min;
second = current_time->tm_sec;
format = 1;
}
// Set the time to these input values
// return = 0 ---> data is valid
// return = 1 ---> something out of range
int time_of_day::set_time(void)
{
return set_time(0, 0, 0);
};
int time_of_day::set_time(int H)
{
return set_time(H, 0, 0);
};
int time_of_day::set_time(int H, int M)
{
return set_time(H, M, 0);
};
int time_of_day::set_time
(int hour_in, int minute_in, int second_in)
{
int error = 0;
if (hour_in < 0) {
hour_in = 0;
error = 1;
} else if (hour_in > 59) {
hour_in = 59;
error = 1;
}
hour = hour_in;
if (minute_in < 0) {
minute_in = 0;
error = 1;
} else if (minute_in > 59) {
minute_in = 59;
error = 1;
}
minute = minute_in;
Page 818-42
Module 818
It should be pointed out that three of the four overloadings actually call
the fourth so that the code did not have to be repeated four times. This
is a perfectly good coding practice and illustrates that other member
functions can be called from within the implementation.
The example program below [USETIME.CPP] is an application that
uses the time class in a rudimentary way.
#include <iostream.h>
#include "date.h"
#include "time.h"
void main()
{
date today;
time_of_day now, lunch(12, 15);
cout << "This program executed on " << today.get_date_string() <<
" at " << now.get_time_string() << "\n";
cout << "We are planning lunch at " << lunch.get_time_string() <<
" tomorrow.\n";
lunch.set_time(13);
cout << "We decided to move lunch to "<< lunch.get_time_string()
<< " due to a late meeting.\n";
}
Module 818
Page 818-44