Objects protect data

The next program [CLASPOLE.CPP] is an example of data protection

in a very simple program.

#include <iostream.h>

class rectangle { // A simple class

int height;

int width;

public:

int area(void); // with two methods

void initialize(int, int);

};

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;

}

struct pole {

int length;

int depth;

};

void main()

{

rectangle box, square;

pole flag_pole;

// 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";

}

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

compiler is now being used to prevent the erroneous calculations. The

end result is that the stupid calculations we did in the last program are

not possible in this program so lines 35 to 38 have been commented

out. Once again, it is difficult to see the utility of this in such a simple

program, but in a large program, using the compiler to enforce the rules

can really pay off.

Even though the square and the box are both objects of class rectangle,

their private data is hidden from each other such that neither can

purposefully or accidentally change the others data. This is the abstract

data type mentioned earlier in this section, a model with an allowable

set of variables for data storage and a set of allowable operations that

can be performed on that stored data. The only operations that can be

performed on the data are those defined by the methods which prevents

many kinds of erroneous or silly operations. Encapsulation and data

hiding bind the data and procedures, or methods, tightly together and

limit the scope and visibility of each. Once again, we have the "divide

and conquer" technique in which an object is separated from the rest of

the code and carefully developed in complete isolation from it. Only

then is it integrated into the rest of the code with a few very simple

interfaces.

A good example of the use of this technique is in file access in ANSI-C.

The data in the file are only available through the predefined functions

provided by your compiler writer. You have no direct access to the

actual data because it is impossible for you to address the actual data

stored on the disk. The data are therefore private data, as far as you are

concerned, but the available functions are very much like methods in

C++. There are two aspects of this technique that really count when

you are developing software. First, you can get all of the data you really

need from the file system because the interface is complete, but

secondly, you cannot get any data that you do not need. You are

prevented from getting into the file handling system and accidentally

corrupting some data stored within it. You are also prevented from

using the wrong data because the functions available demand a serial

access to the data.

Another example is monitor and keyboard handling routines. You are

prevented from getting into the workings of them and corrupting them

accidentally (or even on purpose), but once again, you are provided

with all of the data interfaces you need.

Suppose you are developing a program to analyse some characteristics

of flagpoles. You would not wish to accidentally use some data

referring to where the flagpole program was stored on your hard disk as

the height of the flagpole, nor would you wish to use the cursor

position as the flagpole thickness or colour. All code for the flagpole is

developed alone, and only when it is finished, is it available for external

use. When using it, you have a very limited number of operations which

you can do with the class. The fact that the data is hidden from you

protects you from accidentally doing such a thing when you are

working at midnight to try to meet a schedule. Once again, this is

referred to as information hiding and is one of the primary advantages

of object oriented programming over procedural techniques.

Based on the discussion given above you can see that object-oriented

programming is not really new, since it has been used in a small

measure for as long as computers have been popular. The newest

development, however, is in allowing the programmer to partition her

programs in such a way that she too can practice information hiding and

reduce the debugging time.

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

the object is declared and can therefore be of great help in preventing

the use of an uninitialised variable. When the object named box is

declared in line 36, the constructor is called automatically by the

system. The constructor sets the values of height and width each to 6 in

the object named box. This is printed out for reference in lines 38 and

39. Likewise, when the square is declared in line 36, the values of the

height and the width of the square are each initialised to 6 when the

constructor is called automatically.

A constructor is defined as having the same name as the class itself. In

this case both are named rectangle. The constructor cannot have a

return type associated with it since it is not permitted to have a user

defined return type. It actually has a predefined return type, a pointer to

the object itself, but we will not be concerned about this until much

later in this tutorial. Even though both objects are assigned values by

the constructor, they are initialised in lines 45 and 46 to new values and

processing continues. Since we have a constructor that does the

initialisation, we should probably rename the method named initialize( )

something else but it illustrates the concept involved here.

The destructor is very similar to the constructor except that it is called

automatically when each of the objects goes out of scope. You will

recall that automatic variables have a limited lifetime since they cease to

exist when the enclosing block in which they were declared is exited.

When an object is about to be automatically deallocated, its destructor,

if one exists, is called automatically. A destructor is characterised as

having the same name as the class but with a tilde prepended to the

class name. A destructor has no return type.

A destructor is declared in line 9 and defined in lines 26 to 29. In this

case the destructor only assigns zeros to the variables prior to their

being deallocated, so nothing is really accomplished. The destructor is

only included for illustration of how it is used. If some blocks of

memory were dynamically allocated within an object, a destructor

should be used to deallocate them prior to losing the pointers to them.

This would return their memory to the free store for further use later in

the program.

It is interesting to note that if a constructor is used for an object that is

declared prior to the main program, otherwise known as globally, the

constructor will actually be executed prior to the execution of the main

program. Similarlry, if a destructor is defined for such a variable, it will

execute after the main program has been executed. This will not

adversely affect your programs, but it is worth noting.

 

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;

}

// This method will calculate and return the area of a box

instance

int box::get_area(void)

{

return (length * width);

}

void main()

{

box small, medium, large, group[4]; //Seven boxes to work with

small.set(5, 7);

large.set(15, 20);

for (int index = 1 ; index < 4 ; index++)

//group[0] uses default

group[index].set(index + 10, 10);

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";

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

cout << "The array box area is " <<

group[index].get_area() << "\n";

cout << "The extra data value is " << small.get_extra() << "\n";

cout << "The extra data value is " << medium.get_extra() << "\n";

cout << "The extra data value is " << large.get_extra() << "\n";

cout << "The extra data value is " << group[0].get_extra()

<< "\n";

cout << "The extra data value is " << group[3].get_extra()

<< "\n";

}

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.

 

Variable Decleration and Definition, Dynamically allocate object, and Pointer to object.

 

Links:  Home C Programming Guide  C++ programming Guide

Contact Me