next up previous contents
Next: Data encapsulation (public and Up: Data encapsulation with classes Previous: Data encapsulation with classes   Contents

Classes

To facilitate the definition of abstract data types, the programming language Simula (1967) introduced a concept called a class. (The word class is not important in itself, except that it has stuck and so is now a standard term in the field.)

A class is a ``type definition'' consisting of two parts:

  1. How the data is stored in this type.
  2. What functions are available to manipulate this data.

For instance, we might represent a stack of integers by the following class definition:

  class stack {       
    int values[100];      /* values are stored in an array */
    int tos = 0;          /* top of stack, initialize to 0 */

    push (int i, ...){    /* push i onto stack */
      values[tos] = i;
      tos = tos+1;        /* Should check that tos < 100!! */
    }
    
    int pop (...){        /* pop top of stack and return it */
      tos = tos - 1;      /* Should check that tos > 0!! */
      return values[tos];
    }

    bool is_empty (...){  /* is the stack empty? */
      return (tos == 0);  /* yes iff tos is 0    */
    }
  }

At some places in this hypothetical definition, the function parameters have been left unspecified and denoted ``...''. We will fill in these blanks shortly.

We can use the class name as a new type in the language and define variables of this type elsewhere in the program, as follows:

  stack s,t;

These definitions only provide the capability for s and t to denote stacks. No storage is allocated at this point. They correspond to a pointer declaration of the type int *p in C, which gives p the ability to point to a memory location holding an integer but does not, in itself, allocate a valid memory location for p to point to.

In order to get a ``real'' stack, we have to ask for it. For instance, we might say:

   s = new stack;
   t = new stack;

This generates two independent stacks, one named s and the other t. If, instead, we had written

   s = new stack;
   t = s;

we get a single stack with two names, s and t, either of which can be used to access the stack.

The class definition only provides a template for a datatype. The operation new generates an instance of the template. The word object in object-oriented programming is synonymous with the phrase instance of a class.

How do we manipulate the objects s and t? Suppose we want to push the value 7 onto the stack s. We do this by writing:

    s.push(7);

Note the difference in style with a conventional language. In C or Haskell, push would take two arguments, the stack itself and the value to be pushed. In the object-oriented framework, each instance of the class has its ``own'' copy of these functions and is thus an implicit argument to the function.

Similarly, to retrieve the value at the top of s into a local variable and to query whether t is empty, we would write the following, respectively:

    i = s.top();
    if (t.is_empty()) {...}

We can now say what should appear in place of the mysterious ...in the function definitions in our class stack--nothing! In other words, the functions pop and is_empty take no arguments at all--the only argument they need is the name of the stack on which to operate, which is implicitly given since these functions will be called with respect to a fixed stack. Similarly, push needs only one argument: the value to be pushed.

Is there any significant difference between a C style function push(s,i) which takes two arguments, one of which is the stack, and the object-oriented version s.push(i), where the stack is implicitly passed? Arguably this is only a matter of style and the two approaches have the same ``effect''.

But, philosophically, the syntax reflects the object-oriented approach: when we create an object (an instance of a class) we are supposed to visualize the object as a black box with a display which can show us some information about the box and some slots where we can insert data. The box is equipped with ``buttons'', one for each function. Pushing a button invokes the corresponding function, with the input slots used to slip in parameters to the function, and the value returned shown on the display. Our access to the internal state of the object, whether to observe it or to modify it, is expected to be only via pushing buttons. (Note: More jargon--the functions defined in classes are often called methods and the data items are often called instance variables or instance fields.)


next up previous contents
Next: Data encapsulation (public and Up: Data encapsulation with classes Previous: Data encapsulation with classes   Contents
Madhavan Mukund 2004-04-29