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