1. Classes
a. Philosophy of Classes
b. Syntax of Classes
c. Data Hiding
d. The Scope Operator
e. Instantiations (objects)
f. Learning to
"read" classes
Philosophy of Classes, Object Oriented Programming:
Next (Syntax of Classes)
Classes are a way of encapsulating data, and looking
at the world. To the programmer, they are ways of encapsulating data,
and to the software engineer, or computer scientist, they are ways of looking
at the world. The first thing to realize about classes is that they
are not just things that you have to put in your programs, but they are
a way of programming. Instead of thinking about a program as a procedure
of the computer does this, this, that, this, and then something else, we
can instead think of a program as, the database tells the program that
it has a record for John Smith, and the program reacts to that by outputting
the name John Smith (for example). This is of course a simple example,
but it is none the less important. Instead of merely looking at a
program as a procedure, one can look at it, instead, as an interaction
between objects, and to that end, there is no more free floating programs
in Java, which is highly regarded as the second coming of C++.
So, given that, let us look at a simple example
of two ways of doing things. We have a program segment which must
make a robot push a button depending if the room is too warm, or too cold.
(it is assumed that there is a function which will push the correct button given a certain input)
void adjustTemp(int roomTemp, int *addressOfRobotControl) {
if (roomTemp > MAX_ROOM_TEMP) {
pushButton(addressOfRobotControl, PUSH_COOL_BUTTON);
} else if (roomTemp < MAX_ROOM_TEMP) {
pushButton(addressOfRobotControl, PUSH_HEAT_BUTTON);
} else {
// the room is just the right temp, so do nothing.
}
}
One can immediately see that this is a bit abstracted, though it does not appear very aesthetically pleasing. On the other hand, consider an example done using classes:
assume member functions:
class Robot {
enum ButtonType {
Cool_Button, Heat_Button
};
void pushButton(ButtonType WhichButtonToPush);
};
class Room {
public:
void adjustTemp(int currentTemperature,
Robot & robot);
static int maximum_temperature;
static int minimum_temperature;
};
Now, the implementation is:
void Room::adjustTemp(int currentTemperature, Robot&
robot) {
if (currentTemperature > maximum_temperature)
{
robot.pushButton(Robot::Cool_Button);
} else if (currentTemperature < minimum_temperature)
{
robot.pushButton(Robot::Heat_Button);
} else {
// If the room is the right temperature,
do nothing.
}
}
The real reason that this is better is that there are many of the features of Object-Oriented Programming here, by this we mean programming using the Class world view. Because of the fact that everything is contained within a class, one does not have to worry about conventions such as using all capital letters, which detracts from readability, and using things such as the '::' operator we ensure that we know where from everything comes. Though, it may not be your style, even so, it is good to know anyway, because of the encapsulation idea.
Beyond the philosophy of classes, one must know the syntax. This will be gone over below.
Syntax of Classes
Next(Why Data Hiding?)
Prev(Philosophy) Top
(Classes)
For classes there is an interface of the class,
and an implementation of the class. The interface of the class is
always in a file which is called the same name as the class, with a .h
appended to it. So if the class's name is Pixel, then the file in
which the interface for that class will be in is Pixel.h. The interface
of a class should not contain any implementations for the member functions
for that class. All it should contain are prototypes and member variables.
Everything else should go in the implementation file.
A class has multiple parts, one part, which is preceeded
by a 'public:' keyword, can be accessed and seen by anything,
in or out of the class, variables which are in this area can be modified
by things outside of the class as well. Typically, no variables are
defined in the public section. What typically goes in this section
is function which are used to access features of the class, or function
which view or change the variables of the class in a controlled way, and
update the other variables of the class which, potentially, depend on other
variables of the class when those variables are changed. The other
part is preceeded by a 'private:' keyword. Nothing in this
area can be accessed or modified, outside of the class. This is usually
where member variables are declared, and function which can access the
class, but should not be used by anyone outside the class (these are such
things as auxiliary functions, and default methods which one does not want
to allow people to use, such as the copy constructor and the assignment
operator).
These parts are called the body of the class, and
are only part of the class. The entire class consists of the keyword
class, followed by the name of the class, in the name of the class, one
must specify whether this class inherits from any other class(es), the
name is followed by an open curly brace ( '{' ), the body of the class,
and a closed curly brace, ( '{' ), followed by a semicolon. More
succinctly,
class classname : classToInheritFrom {
<body>
};
ClassToInheritFrom may consist of a list of classes,
each of which may optionally be declared either public or private, meaning
that the things which are inherited from the class can be seen from outside
the world or not. The simplest class, one might think is:
class Foo {
};
As, in this case, it has an empty body, and does not inherit from anything, though similarly, this class cannot do anything either. A more complex class is:
class Foo {
public:
void set_m(int nextM);
int get_m();
private:
int m;
};
This may look a bit unnerving but let us take it apart. This is
a class, we know this because of the 'class' keyword. Further, it
has two member functions which are visible to the world, set_m() and get_m().
These are called accessor functions, because they each access the member
variable m, which is not visible to the world (it is in a private section
of the class, because of the 'private' keyword), one is used to return
the value of m and one to set the value of m to its argument. The
implementation is straight forward for these function due to the method
in which the functions were named, with their purpose explicitly stated.
The above is called the interface of the class,
and should be placed in a file called Foo.h.
The implementation to this class is listed below,
and should be placed in a file called Foo.C. One small note is that
we must use #include "Foo.h" to let the compiler know what a Foo is, because
it assumes that each file is separate, and has no relation to any other
file.
#include "Foo.h"
void Foo::set_m(int nextM) {
m = nextM;
}
int Foo::get_m() {
return m;
}
Why Data Hiding?
Next(The Scope Operator)
Prev(Syntax)
Top(Classes)
So, why do we even have these functions, couldn't we just as simply
replace these functions with what they do? Yes, but consider the
following:
class Baseball {
public:
void pitch();
void hit();
private:
int x, y;
};
#include "Baseball.h"
void Baseball::pitch() {
int num = random() % 100; // Get the random percentage
of doing something on this pitch.
// We say that the catcher is positioned at 0,0,
the pitcher is positioned at 10,10
if (num < 30) { // We have a 30% chance of missing
everything entirely...
// If we miss everything then simply throw it back
to the pitcher...ignore wild throw benefit.
x = 10;
y = 10;
} else if (num < 50) { // We have another 20% chance of hitting
the batter...
// if we hit the batter, the pitcher gets the ball
back also...
x = 10;
y = 10;
} else if (num < 80) { // We actually have a 30% chance of
getting a strike...
// If it is a strike then leave the ball at the
catcher, as someone might try and steal...
x = 0;
y = 0;
} else {
hit();
}
}
void Baseball::hit() {
int num = random() %100; // Get the random percentage
of what we did with the ball.
if (num < 10) { // We have a ten percent chance of fouling
off the ball...
x = 10;
y = 10;
} else if (num < 30) { // We have another 20% chance
of hitting the ball into the infield
x = 13;
y = 12;
} else if (num < 50) { // Another 20% chance of hitting
a Pop Fly
x = 10;
y = 10;
} else if (num < 80) { // 30% chance of hitting a grounder
out past the infield...
x = 23;
y = 20;
} else if (num < 90) { // 10% chance of hitting a home run...
x = 10;
y = 10;
} else { // We missed...leave it at the catcher.
x = 0;
y = 0;
}
}
This is a simple example, and one can see quickly that simply setting
something outside the object, possibly by an agent which does not know
the internal workings of the object, could be dangerous, and have problems
with future runs of the functions.
Syntax of Classes and the Scope Operator
(::)
Next(Instantiation)
Prev(Why Data Hiding?)
Top(Classes)
The scope operator deserves explanation, as I used
it multiple times above, yet have not explained it. Consider the
following codelet:
int Foo::get_m() { return m; }
The scope operator is in between Foo and get_m, but what does it do?
In short it changes your scope to be 'inside of' Foo. Normally, a
function cannot see m, though when you use the scope operator, you can
suddenly access it. Unfortunately, and fortunately, you still cannot
modify or read internal variables with the scope operator if you are not
in a member function (previously defined in the class interface) of the
class. Though this allows you to tell the compiler that a function
is really a member of a class, and not just a function out there, because
recall that a one can have both:
void get_m();
and
class Foo() {
get_m();
};
and that both of these are different functions. This is actually due to the overloading behavior of C++, allowing a function to be specified multiple times, so long as it has a different function signature each time. In short what this means, is that we can have the above specification because in reality, the two functions are:
void get_m();
and:
void Foo::get_m();
Instantiations
Next(Learning to Read
Classes) Prev(Scope
Operator) Top(Classes)
I have previously gone over the purely abstract qualities
of Classes, and more to the point, have not really gone over how to use
them. In order to use a class, you must create an object, an actual
instantiation of that class. This is actually quite easy, and all
it requires is to use the class name as the variable type in a normal variable
declaration, i.e. if your class's name is Foo, then:
Foo fooObject;
Would create a variable, called fooObject, which is an object of the
type class Foo. This object can be used to call any of the member
functions of the Foo object, and further, has member variables within it
(defined in the class definition) which may be modified, or read.
Consider the following definition:
class Foo {
public:
Foo(); // Default constructor, place foo
in the position (1,1) and give him 30 health.
// Put a Foo in a specific location, give him
30 health.
Foo(int x, int y);
// Put a Foo in a specific location and give
him h health.
Foo(int x, int y, int h);
// These are the directions in which a Foo can
move.
enum direction {
NORTH, EAST, SOUTH, WEST
};
// Move the Foo in a specified direction.
void move(direction whereToMove);
private:
int health; // How healthy is this Foo?
int x, y; // Where is this Foo? (X,Y coordinates)
};
So, in order to call move(Foo::NORTH), we would have to have an object of type Foo, as the scope operator will not help us. This is because move is a function of type void, and therefore (because it is not of type static void) must have an instance which it can act on. So, while:
int main() {
Foo::move(Foo::NORTH);
return 0;
}
Would not work (we actually see this because we have not specified what must move, only that whatever must move, it must be a Foo...), though the following would:
int main() {
Foo myFirstFoo;
myFirstFoo.move(Foo::NORTH);
return 0;
}
Because it is telling the Foo myFirstFoo to move to whatever a Foo calls
NORTH. This is essentially how you must learn to read the lines.
Let me move more step-by-step to give you an understanding of what I am
talking about.
Learning to "read" classes
Next()
Prev(Instantiation)
Top(Classes)
Reading classes is just like reading code, only a
specific case of doing such. Reading any line of Code is actually
not too difficult. One must simply learn how to parse the symbols
correctly. Consider the following example from above.
void Foo::get_m() {
return m;
}
This would be read as follows: There is a function (see the '('
and ')' symbols just after get_m), which returns void (the first thing
on the line), it is called get_m (the actual name of the function, after
the return type) that 'belongs to' or is 'inside of' Foo (The scope operator
(::) should be read as inside of, though sometimes it can also be read
as belongs to). This function is defined as ( read the '{' ) returning
m (the return m line) (and read the '}' just as you would a period).
This is naturally an implementation of the member function of Foo, called
get_m. Now, moving onward, consider the following (as Foo was defined
in the previous section):
int main() {
Foo m;
m.move(Foo::NORTH);
return 0;
}
This should be read as follows, there is a function called main, that
returns an int. It is defined as: a variable of type Foo is
created, using a default initialization (this is parsed this way because
Foo is the type of the variable, and m follows it, though there are no
parentheses, so we assume that it is being initialized using the default
constructor), then m is told to move to whatever a Foo calls NORTH (move
is a member function of m, we use the phrase m is told to move because
by calling a member function we are sending m a message, namely, that it
must move, further, where we tell it to move is to the NORTH, but we aren't
sure what a Foo considers NORTH, so we tell it to move in whatever direction
is NORTH to it (Foo::NORTH, specified the NORTH inside of Foo).), and that
finally, we wish to return from this function (which because it is main
will exit the program).