Copyright © 2012 David Schmidt

Lecture 3:
Classes and Objects


3.1 Computer storage layout
    3.1.1 Example with static vars and an array object
    3.1.2 Example with a class
3.2 Object diagrams for design


What are these things? Which are values? types? objects? handles? classes? (Note: people use different formal definitions for these notions.)

A class is a template for allocating a frame (namespace) and defining functions (methods) that access the frame. You won't get into too much trouble by pretending that a class is a ``user-defined data type.'' (For most people, a "data type" is a collection of values and some operations that compute on the collection.)

An object is the frame ("namespace") that is allocated when you construct a new instance of a class template. A static variable or method is a single instance that is "pre-declared" (and not constructed).


3.1 Computer storage layout

A conventional computer uses linear primary storage. When you start a computer and then start, say, a web browser, storage looks somewhat like this:
+-----------------------+------------------+------------------------+
| operating system code | web browser code | ... ( free space ) ... |
+-----------------------+------------------+------------------------+
Say you start a C# program, which is an .exe file. Some of the free space is used. The program (.exe file) itself is loaded and there is space reserved for a "static area", a "stack", and a "heap". The layout looks like this:
+-- ... --+------------+-------------+----------------------------+------------------------------------------+
| OS etc. | C# program | static area | stack --> (empty) <-- heap | ...(remaining free space)... |
+-- ... --+------------+-------------+----------------------------+------------------------------------------+
The code and static vars are copied into fixed segments. The stack and heap both grow while the program executes, so they are laid out at opposite ends of the allocated segment. (If they grow together, the OS is asked to allocate more storage for the program --- we won't worry about this!)

When a new object is allocated, it is allocated in the heap part.

When a method is called, its parameter-argument bindings and its local variables are saved in a new frame that is allocated "on top of" the stack. (When the method finishes, the frame is "popped" from the stack.)

In fact, a frame and an object are the same thing, but they are allocated at different places.


3.1.1 Example with static vars and an array object

Draw the storage layout for this example, when the execution reaches //***:
===================================================

using System;
public class Prog {
     public static int size = 4;
     
     public static void Main() {
       int[] r = new int[size];
       r[0] = size;
       int j = size - 1;
       r[j] = f(j);
     }

     public static int f(int x) {  
       //***
       return size + x;
     }
   }
}

===================================================
Now run VS on the example and break at point //*** to see how VS presents the storage layout in its Debug windows; compare it to the storage layout:

VS does not show the difference between the stack and the heap! Also, it is not so good at displaying static variables.


3.1.2 Example with a class

Draw the storage layout for this example, when the execution reaches //***:
===================================================

using System;
public class Ex2 {
   public static void Main() {
     Clock c = new Clock(80);
     Clock d = new Clock(90);
     c.tick(2);
     Clock e = d;
     d.tick(3);
     Console.WriteLine(e.getTime());  Console.ReadLine();
   }
}

class Clock {
    static int count = 0;
    private int t = 0;
    public Clock(int start) { t = start; count = count + 1;
    }
    public void tick(int n) {
        ///***
        t = t + n;
    }
    public int getTime() { return t; }
}

===================================================
Here is the drawing, when the breakpoint is reached the second time, due to the call, d.tick(3):

Notice that the methods for Clocks are saved in the Code area, and not in the objects. (Doing the latter would be correct but a waste of storage.) This little optimization causes a complication --- see below.

Now run VS on the example and break at point //*** to see how VS presents the storage layout. Match it to the above diagram.

Visual Studio is not so good at showing the handles (storage-location numbers) of objects, and it does not show that c and e are names that share (alias) the same object. Be careful!

Now, consider the code in tick:

t = t + n ;
Variable n is local and saved in tick's frame. But t is nowhere to be seen. To find it, this is used: t is treated as this.t, which leads to the correct variable. The value of this was set in tick's frame by the call,
d.tick(3);
The value (handle) of d becomes the value of this. Indeed, you can read d.tick(3) as shorthand for this call: tick(d, 3) --- that's how the C# compiler reformats the call. Also, the C# compiler reformats the body of method tick to look like the following:
public void tick(Clock this, int n) {
  this.t = this.t + n;
}
because the C# compiler sees in advance that t is not locally declared in method tick, so t must be this.t!

This setup lets the code for tick work correctly with either object c or object d (or object e).


3.2 Object diagrams for design

The storage layout pictures are called object diagrams. They are used when we analyze executions. But object diagrams are also useful when we design large systems, because they are a kind of "blueprint" of how computer storage will be used by a system.

When an object diagram is used for design, only the Heap portion is drawn, and details about fields within objects can be omitted.

Here is an example: We hope to build a database of bank accounts and customers, where a customer might own multiple bank accounts. The data base also has a global clock and some main controller that enforces access rules to the customer and account records. Here is an object diagram that shows how the Heap might look once the system is executing:


The arrows are pointers (handles) held in objects that connect to other objects. Array-like collections are drawn as vectors. Objects are labelled, :CLASSNAME, since we will be coding classes to generate the objects.

Another example: we are designing a card game that uses Cards, a Deck to hold the cards, and a Hand-of-Cards for each Player. Here is a design expressed as an object diagram:


When you code this system, you compare the storage layouts generated by the execution to the object diagram.

There is more to say about this next time.

Exercise