CS 250 Computer Programming and Problem Solving - FALL 1998 

[ Home | Syllabus | Notes | Glossary | CS250 WEB Version Home Page ]


 

Class on Classes with Class


Objects and Classes

 

 


What is an Object?

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:

  1. Objects arise naturally in the problem domain.(relates to the end user)
  2. Objects are a good packaging concept. (good programming construct)
  3. Objects can be reused easily (they are good building blocks).

 

 


Object Terminology

OBJECT
A thing with STATE and BEHAVIOR
STATE
The VALUEs of an OBJECTs PROPERTIES
PROPERTIES (ATTRIBUTES)
Things which describe an OBJECT
IDENTITY
The name of an OBJECT
BEHAVIOR (OPERATIONS)
The things an object can do
CLASS
A set of OBJECTs which share the same properties and behaviors

Objects Should be Well-Behaved (that is why the basic operations are packaged with the properties)

Familiar example: The "int" OBJECT

PROPERTY is the VALUE of the integer
int i = 5; // OBJECT "i" has the PROPERTY VALUE 5
The IDENTITY of this OBJECT is "i"
that is its name
The CLASS of this OBJECT
int
The BEHAVIOR of this OBJECT are all the functions defined for "int"s
+, -, /, *, %, ++, --

Class Terminology

CLASS
A set of OBJECTs with common PROPERTIES and BEHAVIORS
INSTANCE
is a single member of a CLASS (aka OBJECT)
MEMBER DATA
The PROPERTIES of an OBJECT as defined in the CLASS
MEMBER FUNCTIONS
The BEHAVIORS of an OBJECT as defined in the CLASS

Each INSTANCE (or OBJECT) has its own copies of the MEMBER DATA,
But all INSTANCES in the same class share the MEMBER FUNCTIONS.


Why Program With Objects?

It is good packaging:

  1. It keeps the data and basic functions needed to change the data together.

  2. It allows you to hide (protect) parts of the object from the user

  3. 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
rainbow.gif (2243 bytes)

   int number1;
   int number2;
   int number3;

rainbow.gif (2243 bytes)
Not too good, huh? WHY is this a bad choice?
rainbow.gif (2243 bytes)

   int x;
   int y;
   int display;

rainbow.gif (2243 bytes)
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.
rainbow.gif (2243 bytes)

   struct Coordinate {
      int x;
      int y;
      int display;
   }

rainbow.gif (2243 bytes)
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
rainbow.gif (2243 bytes)

   Coordinate point1;
   Coordinate point2;

rainbow.gif (2243 bytes)

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
rainbow.gif (2243 bytes)

   point1.x = 30;
   point2.display = 0;
   cout << point1.x;

rainbow.gif (2243 bytes)
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.
rainbow.gif (2243 bytes)

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

rainbow.gif (2243 bytes)
Now we have made an object class out of the coordinates. To get individual object instance, we just do as we did before.
rainbow.gif (2243 bytes)

   Coordinate point1;
   Coordinate point2;

rainbow.gif (2243 bytes)
To set the values safely, you can use the newly provided functions.
rainbow.gif (2243 bytes)

   point1.setX(30);
   point2.setDisplay(45); // a mistake but it will be fixed
   cout << point1.x;

rainbow.gif (2243 bytes)
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
rainbow.gif (2243 bytes)

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

rainbow.gif (2243 bytes)
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.
rainbow.gif (2243 bytes)

 

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

rainbow.gif (2243 bytes)
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:

  1. All Data Members should be private (so a clumsy programmer cannot put inconsistent information there).
  2. For every private data member, there is usually a 'set' function and a 'get' function

Here is a complete program which uses this class.
You can get your own, compilable copy by clicking here.

rainbow.gif (2243 bytes)

 

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();
}

Function Prototypes in Class Declarations

In the previous example for the "coordinate" class, the member functions were defined right in the class. (see copy of this class below).

 

rainbow.gif (2243 bytes)

 

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

rainbow.gif (2243 bytes)
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.

rainbow.gif (2243 bytes)

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

rainbow.gif (2243 bytes)

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.

rainbow.gif (2243 bytes)

      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;
      }
rainbow.gif (2243 bytes)

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

Types of Member Functions

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

Constructors:
Special functions that are called whenever a new instance of an object is created. It is used to set the initial values for the data members of the class. There can be several different constructor functions for the same class. This is explained below.
 
Destructors:
Special functions that are called whenever an instance of an object is about to be destroyed. (for instance if it is a local variable in a function that is ready to return). They are used to clean up any dynamic storage that may have been allocated (remember every "new" operator should have a corresponding "delete"). You never call destructors. The operating system calls them when necessary.
 
Selectors:
Functions that return the value of the data members (which are usually private and therefore need public selector functions). In the "coordinate" example, 'getX', 'getY' and 'getDisplay' were selector functions.
 
Modifiers:
Functions that modify the private data members. In the "coordinate" example, 'setX', 'setY' and 'setDisplay' were selector functions. In general, modifiers can execute a complex algorithm to compute the modified value, like calculating the grade point average for a student object and storing it.
 
Operators:
Functions that have a special syntax and are used extensively in C++ statements. For example, "=, >, <<, +" are common C++ operators. Many operators have two arguments and thus are called binary operators. Binary operators are frequently written as "infix" functions, in that the are "in" the middle of their two operators, like "a + b" instead of the usual pre-fix syntax of normal functions (e.g. "add(a,b,)"). You will learn more about operators later.
 
Iterators:
Functions that process collections of data, like lists. You will see these later also (they are used in CS361 for sure).

 


Copyright chris wild 1998.
For problems or questions regarding this website contact [Chris Wild (e-mail:wild@cs.odu.edu].
Last updated: August 31, 1998.