Declaration and definition of a variable

An extra variable was included for illustration, the one named

extra_data. Since the keyword static is used to modify this variable in

line 5, it is an external variable and only one copy of this variable will

ever exist. All seven objects of this class share a single copy of this

variable which is global to the objects defined in line 33.

The variable is actually only declared here which says it will exist

somewhere, but it is not defined. A declaration says the variable will

exist and gives it a name, but the definition actually defines a place to

store it somewhere in the computers memory space. By definition, a

static variable can be declared in a class header but it cannot be defined

there, so it is defined somewhere outside of the header, usually in the

implementation file. In this case it is defined in line 12 and can then be

used throughout the class.

Line 17 of the constructor sets the single global variable to 1 each time

an object is declared. Only one assignment is necessary so the other six

are actually wasted code. To illustrate that there is only one variable

shared by all objects of this class, the method to read its value also

increments it. Each time it is read in lines 45 to 51 it is incremented, and

the result of the execution proves that there is only a single variable

shared by all objects of this class. You will also note that the method

named get_extra( ) is defined within the class declaration so it will be

assembled into the final program as inline code.

 

A string within an object

Look at the next program [OBJSTRNG.CPP] for our first example of

an object with an embedded string (actually an embedded pointer, but

the two work so closely together that we can study one and understand

both).

#include <iostream.h>

class box {

int length;

int width;

char *line_of_text;

public:

box(char *input_line); //Constructor

void set(int new_length, int new_width);

int get_area(void);

};

box::box(char *input_line) //Constructor implementation

{

length = 8;

width = 8;

line_of_text = input_line;

}

// 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)

{

cout << line_of_text << "= ";

return (length * width);

}

void main()

{

box small("small box "), //Three boxes to work with

medium("medium box "),

large("large box ");

small.set(5, 7);

large.set(15, 20);

cout << "The area of the ";

cout << small.get_area() << "\n";

cout << "The area of the ";

cout << medium.get_area() << "\n";

cout << "The area of the ";

cout << large.get_area() << "\n";

}

You will notice that line 5 contains a pointer to a string named

line_of_text. The constructor contains an input parameter which is a

pointer to a string which will be copied to the string named line_of_text

within the constructor. We could have defined the variable line_of_text

as an actual array in the class, then used strcpy( ) to copy the string into

the object and everything would have worked the same, but we will

leave that as an exercise for you at the end of this section. It should be

pointed out that we are not limited to passing a single parameter to a

constructor. Any number of parameters can be passed, as will be

illustrated later.

You will notice that when the three boxes are declared this time, we

supply a string constant as an actual parameter with each declaration

which is used by the constructor to assign the string pointer some data

to point to. When we call get_area( ) in lines 38 to 42, we get the

message displayed and the area returned. It would be prudent to put

these operations in separate methods since there is no apparent

connection between printing the message and calculating the area, but it

was written this way to illustrate that it can be done. What this really

says is that it is possible to have a method that has a side effect, the

message output to the monitor, and a return value, the area of the box.

 

An object with an internal pointer

The next program [OBJINTPT.CPP] is our first example program with

an embedded pointer which will be used for dynamic allocation of data.

#include <iostream.h>

class box {

int length;

int width;

int *point;

public:

box(void); //Constructor

void set(int new_length, int new_width, int stored_value);

int get_area(void) {return length * width;} // Inline

int get_value(void) {return *point;} // Inline

~box(); //Destructor

};

box::box(void) //Constructor implementation

{

length = 8;

width = 8;

point = new int;

*point = 112;

}

// This method will set a box size to the input parameters

void box::set(int new_length, int new_width, int stored_value)

{

length = new_length;

width = new_width;

*point = stored_value;

}

box::~box(void) //Destructor

{

length = 0;

width = 0;

delete point;

}

void main()

{

box small, medium, large; //Three boxes to work with

small.set(5, 7, 177);

large.set(15, 20, 999);

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

cout << "The small box stored value is " <<

small.get_value() << "\n";

cout << "The medium box stored value is " <<

medium.get_value() << "\n";

cout << "The large box stored value is " <<

large.get_value() << "\n";

}

In line 5 we declare a pointer to an integer variable, but it is only a

pointer: there is no storage associated with it. The constructor therefore

allocates an integer type variable on the heap for use with this pointer in

line 18. It should be clear to you that the three objects created in line 35

each contain a pointer which points into the heap to three different

locations. Each object has its own dynamically allocated variable for its

own private use. Moreover, each has a value of 112 stored in its

dynamically allocated data because line 18 stores that value in each of

the three locations, once for each call to the constructor.

In such a small program, there is no chance that we will exhaust the

heap, so no test is made for unavailable memory. In a real production

program, it would be essential to test that the value of the returned

pointer is not NULL to ensure that the data actually did get allocated.

The method named set( ) has three parameters associated with it and

the third parameter is used to set the value of the new dynamically

allocated variable. There are two messages passed, one to the small box

and one to the large box. As before, the medium box is left with its

default values.

The three areas are displayed followed by the three stored values in the

dynamically allocated variables, and we finally have a program that

requires a destructor in order to be completely proper. If we simply

leave the scope of the objects as we do when we leave the main

program, we will leave the three dynamically allocated variables on the

heap with nothing pointing to them. They will be inaccessible and will

therefore represent wasted storage on the heap. For that reason, the

destructor is used to delete the variable which the pointer named point

is referencing as each object goes out of existence. In this case, lines 29

and 30 assign values to variables that will be automatically deleted.

Even though these lines of code really do no good, they are legal

statements.

Actually, in this particular case, the variables will be automatically

reclaimed when we return to the operating system because all program

cleanup is done for us at that time. If this were a function that was

called by another function however, the heap space would be wasted.

This is an illustration of good programming practice, that of cleaning up

after yourself when you no longer need some dynamically allocated

variables.

One other construct should be mentioned again, that of the inline

method implementations in line 9 and 10. As we have already seen,

inline functions can be used where speed is of the utmost in importance

since the code is assembled inline rather than by actually making a

method call. Since the code is defined as part of the declaration, the

system will assemble it inline, and a separate implementation for these

methods is not needed. If the inline code is too involved, the compiler is

allowed to ignore the inline request and will actually assemble it as a

separate method, but it will do it invisibly to you and will probably not

even tell you about it.

Remember that we are interested in using information hiding and inline

code prevents hiding of the implementation, putting it out in full view.

Many times you will be more interested in speeding up a program than

you are in hiding a trivial implementation. Since most inline methods are

trivial, you should feel free to use the inline code construct.

 

A dynamically allocated object

Examine the next program [OBJDYNAM.CPP] for our first look at a

dynamically allocated object.

#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);

};

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;

}

// 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; //Three boxes to work with

box *point; //A pointer to a box

small.set(5, 7);

large.set(15, 20);

point = new box;

// Use the defaults supplied by the constructor

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

cout << "The new box area is " << point->get_area() << "\n";

point->set(12, 12);

cout << "The new box area is " << point->get_area() << "\n";

delete point;

}

In line 30 we declare a pointer to an object of type box and since it is

only a pointer with nothing to point to, we dynamically allocate an

object for it in line 33, with the object being created on the heap just

like any other dynamically allocated variable. When the object is created

in line 33, the constructor is called automatically to assign values to the

two internal storage variables. Note that the constructor is not called

when the pointer is declared since there is nothing to initialise; it is

called when the object is allocated.

Reference to the components of the object are handled in much the

same way that structure references are made, through use of the pointer

operator as illustrated in lines 38 to 41. Of course you can use the

pointer dereferencing method without the arrow such as

(*point).set (12, 12);

as a replacement for line 39 but the arrow notation is much more

universal and should be used. Finally, the object is deleted in line 41 and

the program terminates. If there were a destructor for this class, it

would be called as part of the delete statement to clean up the object

prior to deletion.

 

An object with a pointer to another object

The program below [OBJLIST.CPP] contains an object with an internal

reference to another object of its own class. This is the standard

structure used for a singly linked list and we will keep the use of it very

simple in this program.

#include <iostream.h>

class box {

int length;

int width;

box *another_box;

public:

box(void); //Constructor

void set(int new_length, int new_width);

int get_area(void);

void point_at_next(box *where_to_point);

box *get_next(void);

};

box::box(void) //Constructor implementation

{

length = 8;

width = 8;

another_box = NULL;

}

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

}

// This method causes the pointer to point to the input parameter

void box::point_at_next(box *where_to_point)

{

another_box = where_to_point;

}

// This method returns the box the current one points to

box *box::get_next(void)

{

return another_box;

}

void main()

{

box small, medium, large; //Three boxes to work with

box *box_pointer; //A pointer to a box

small.set(5, 7);

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

small.point_at_next(&medium);

medium.point_at_next(&large);

box_pointer = &small;

box_pointer = box_pointer->get_next();

cout << "The box pointed to has area " <<

box_pointer->get_area() << "\n";

}

The constructor contains the statement in line 17 which assigns the

pointer the value of NULL to initialise the pointer. This is a good idea

for all of your programming: don't allow any pointer to point off into

space, but initialise all pointers to something. By assigning the pointer

within the constructor, you guarantee that every object of this class will

automatically have its pointer initialised. It will be impossible to

overlook the assignment of one of these pointers.

Two additional methods are declared in lines 9 and 10 with the one in

line 10 having a construct we have not yet mentioned. This method

returns a pointer to an object of the box class. As you are aware, you

can return a pointer to a struct in ANSI-C, and this is the parallel

construct in C++. The implementation in lines 36 to 39 returns the

pointer stored within the object. We will see how this is used when we

get to the actual program.

An extra pointer named box_pointer is declared in the main program for

use later and in line 50 we make the embedded pointer within the small

box point to the medium box, and in line 50 we make the embedded

pointer within the medium box point to the large box. We have

effectively generated a linked list with three elements. In line 52 we

make the extra pointer point to the small box. Continuing in line 53 we

use it to refer to the small box and update it to the value contained in

the small box which is the address of the medium box. We have

therefore traversed from one element of the list to another by sending a

message to one of the objects. If line 53 were repeated exactly as

shown, it would cause the extra pointer to refer to the large box, and

we would have traversed the entire linked list which is only composed

of three elements.

 

 Keyword THIS, and Link list to objects.

 

Links:  Home C Programming Guide  C++ programming Guide

Contact Me