CS 250 Computer Programming and Problem Solving - FALL 1998 |
---|
[ Home | Syllabus | Notes | Glossary | CS250 WEB Version Home Page ]
Nouns & Verbs: Objects are Nouns amd Verbs denote actions on objects or they describe the attributes of an object.
Definition (Booch): object is something that has state, behaviour and identity
Definition (Martin/Odell): an object is anything real or abstract, about which we store data and those methods(operations) that manipulate the data.
There seems to be increasing interest in object oriented programming for the following reasons:
Objects Should be Well-Behaved (that is why the basic operations are packaged with the properties)
Each INSTANCE (or OBJECT) has its own copies of the MEMBER DATA,
But all INSTANCES in the same class share the MEMBER FUNCTIONS.
It is good packaging:
It keeps the data and basic functions needed to change the data together.
It allows you to hide (protect) parts of the object from the user
It allows you to reuse the object to more easily build related objects.
Motivation by example: Consider the problem of dealing with screen coordinates on a computer display.
What do you need to know about these coordinates?
To make simpler, let's deal with a monochrome display (blank and white).
So we need three variables to keep the information. How about
int number1; int number2; int number3;
Not too good, huh? WHY is this a bad choice?
int x; int y; int display;
Well, it is better but not clear that these three go together to define a single
screen coordinate.
Also, what if you wanted a second Coordinate (to represent the end points of a
straight line)?
Let's package them a little differently.
struct Coordinate { int x; int y; int display; }
Now it is clear that the three variables go together and they are parts of a bigger
structure called "coordinate".
Also, now is is possible to have many coordinates, for example
Coordinate point1; Coordinate point2;
The declaration of the struct Coordinate introduces a new collection of
things into the program of which we you can many instances (represented as individual
variables of that structure type). To set values in an instance, you use the dot '.'
operator. For example
point1.x = 30; point2.display = 0; cout << point1.x;
But there are still problems.
What if you want to make sure that the programmer using this structure does not put
ridiculous values in the fields? Say that the only values that make sense for
"display" are 0 for black and 1 for white, and that the x and y coordinates
cannot be negative or greater than 1000?
Can you control the programmer so that they don't mess up what the values mean?
Well you can if you use a generalization of structures called classes. Now you not
only package the data together, but also the functions that changes the data which you
write to make sure that no bad values are put in the data fields.
class Coordinate { void setX(int xValue) { if(xValue < 0) xValue = 0; // clip values if(xValue > 1000) xValue = 1000; x = xValue; } void setY(int yValue) { if(yValue < 0) yValue = 0; // clip values if(yValue > 1000) yValue = 1000; x = yValue; } void setDisplay(int dValue) { if(dValue < 0) dValue = 0; // clip values if(dValue > 1) dValue = 1; display = dValue; } int x; int y; int display; }
Now we have made an object class out of the coordinates. To get individual object
instance, we just do as we did before.
Coordinate point1; Coordinate point2;
To set the values safely, you can use the newly provided functions.
point1.setX(30); point2.setDisplay(45); // a mistake but it will be fixed cout << point1.x;
Are we there yet? no but we are close.
First of all, the above code won't work in C++ and it is for a good reason.
If we allow access to point1's 'x' value (like we did to print it out), then another
program could just set the value directly, like we did with the structure implementation,
and by pass the safe function 'setX' altogether. So if we really want to protect the
values of 'x', 'y' and 'display', we need to make them 'private'. This principle of hiding
information from programs is an important part of object oriented design. In fact, unless
you specify otherwise, all members of a class (functions and data) are private by default.
Of course if everything is private, then the class cannot be used by
anyone. So something's have to be public (like 'setX'). This is done by declaring certain
members of the class to be 'public' and others 'private'. Now we have
class Coordinate { public: void setX(int xValue) { if(xValue < 0) xValue = 0; // clip values if(xValue > 1000) xValue = 1000; x = xValue; } void setY(int yValue) { if(yValue < 0) yValue = 0; // clip values if(yValue > 1000) yValue = 1000; x = yValue; } void setDisplay(int dValue) { if(dValue < 0) dValue = 0; // clip values if(dValue > 1) dValue = 1; display = dValue; }
private: int x; int y; int display; }
Now what? Well, we can safely set the values of the object, but we can ever see them
again! We could make the data members public also, but this defeats the security we want
in setting their values.
What we need are public functions which will let use see the values.
class Coordinate { public: void setX(int xValue) { if(xValue < 0) xValue = 0; // clip values if(xValue > 1000) xValue = 1000; x = xValue; } void setY(int yValue) { if(yValue < 0) yValue = 0; // clip values if(yValue > 1000) yValue = 1000; x = yValue; } void setDisplay(int dValue) { if(dValue < 0) dValue = 0; // clip values if(dValue > 1) dValue = 1; display = dValue; } int getX() { return x; } int getY() { return y; } int getDisplay() { return display; }
private: int x; int y; int display; }
Since the member functions belong to the class, they are allowed to access the
private data members.
Does this seem like a lot of work to get a little safety? Perhaps, but this is a
simple example. As obejcts get more complicated, it is nice to have everything packaged
and protected. There will be more to say about protected both data and function members
later, when we talk about friends and inheritance.
Here are some general guidelines that we used in this example that apply to most classes:
|
Here is a complete program which uses this class.
You can get your own, compilable copy by clicking
here.
class Coordinate { public: void setX(int xValue) { if(xValue < 0) xValue = 0; // clip values if(xValue > 1000) xValue = 1000; x = xValue; } void setY(int yValue) { if(yValue < 0) yValue = 0; // clip values if(yValue > 1000) yValue = 1000; x = yValue; } void setDisplay(int dValue) { if(dValue < 0) dValue = 0; // clip values if(dValue > 1) dValue = 1; display = dValue; } int getX() { return x; } int getY() { return y; } int getDisplay() { return display; } private:
int x; int y; int display; };
#include <iostream.h> void main() { Coordinate p1; Coordinate p2;
p1.setX(37); cout << p1.getX(); }
In the previous example for the "coordinate" class, the member functions were defined right in the class. (see copy of this class below).
class Coordinate { public: void setX(int xValue) { if(xValue < 0) xValue = 0; // clip values if(xValue > 1000) xValue = 1000; x = xValue; } void setY(int yValue) { if(yValue < 0) yValue = 0; // clip values if(yValue > 1000) yValue = 1000; x = yValue; } void setDisplay(int dValue) { if(dValue < 0) dValue = 0; // clip values if(dValue > 1) dValue = 1; display = dValue; } int getX() { return x; } int getY() { return y; } int getDisplay() { return display; } private: int x; int y; int display; }
But as the functions get more complicated, reading the class declaration gets more
difficult. One way to remedy this difficulty is to use a function prototype which gives
the essentials of the function and to define the function elsewhere.
This has another great benefit when building large systems in that you can define the member functions in a separate file and compile them and put them into libraries before the program that uses these class functions are even written.
In fact you can distribute these libraries and prevent users from seeing (and copying ) your original source code. And for large systems separate compilation is the only way to go.
We will talk about separate compilation later, first let's simpilify the class declaration and show you how to later define the member functions.
class Coordinate { public: void setX(int xValue); void setY(int yValue); void setDisplay(int dValue); int getX(); int getY(); int getDisplay(); private: int x; int y; int display; }
Moving the definition of the member functions outside the class declaration introduces a little naming conflict. How do I know that there isn't another 'setX' function floating around? (In a large system, lots of programmers will tend to use similar names).
The solution is to use the Scope Resolution Operator ('::') and
to put the class name in front and the member name after.
It looks a little funky but it works for the compiler. So continuing our example.
void Coordinate::setX(int xValue) { if(xValue < 0) xValue = 0; // clip values if(xValue > 1000) xValue = 1000; x = xValue; } void Coordinate::setY(int yValue) { if(yValue < 0) yValue = 0; // clip values if(yValue > 1000) yValue = 1000; x = yValue; } void Coordinate::setDisplay(int dValue) { if(dValue < 0) dValue = 0; // clip values if(dValue > 1) dValue = 1; display = dValue; } int Coordinate::getX() { return x; } int Coordinate::getY() { return y; } int Coordinate::getDisplay() { return display; }
Here is a complete example (click here to get your own copy)
class Coordinate { public: void setX(int xValue); void setY(int yValue); void setDisplay(int dValue); int getX(); int getY(); int getDisplay(); private:
int x; int y; int display; };
#include <iostream.h> void main() { Coordinate p1;
p1.setX(37); cout << p1.getX(); }
void coordinate::setX(int xValue) { if(xValue < 0) xValue = 0; // clip values if(xValue > 1000) xValue = 1000; x = xValue; } void coordinate::setY(int yValue) { if(yValue < 0) yValue = 0; // clip values if(yValue > 1000) yValue = 1000; x = yValue; } void coordinate::setDisplay(int dValue) { if(dValue < 0) dValue = 0; // clip values if(dValue > 1) dValue = 1; display = dValue; } int coordinate::getX() { return x; } int coordinate::getY() { return y; } int coordinate::getDisplay() { return display; }
Previously we saw that a class in C++ can contain functions,
called member functions.
In the "coordinate"
class, we used 'set' and 'get' functions and mentioned that this is a common practice.
In fact, we use special names for the different roles that member functions play in a
class