Worksheet Object-Oriented Programming

Questions and Completion

To receive credit for completing the worksheet, you must complete the corresponding quiz (it is just a checkbox) on D2L when you have finished the worksheet.

If you have questions as you go through this worksheet, please feel free to post them on the discussion forum.

Inheritance

The Java programs in this section use implementation inheritance, abstract methods, dynamic dispatch, and super calls.

Example 1

Consider the following Java program. Work out what will be printed when it runs.

Verify your answer by compiling and running the program.

abstract class A {
  int f (int x) {
    System.out.format ("A.f (%d)%n", x);
    return (x == 0) ? g () : f (x - 1); 
  }

  abstract int g ();  

  void h () {
    System.out.println ("A.h ()"); 
  }
}


class B extends A {
  int g () { 
    System.out.println ("B.g ()"); 
    A r = this;
    r.h ();
    return 0;
  }

  void h () {
    System.out.println ("B.h ()"); 
    super.h ();
  }
}


public class Inheritance07 {
  public static void main (String[] args) {
    A x = new B (); 
    x.f (3);
  }
}

Example 2

Consider the following Java program. Work out what will be printed when it runs.

Verify your answer by compiling and running the program.

class A {
  int f (int x) {
    System.out.format ("A.f (%d)%n", x);
    return (x == 0) ? g () : f (x - 1); 
  }

  int g () {
    System.out.println ("A.g ()"); 
    return 0;
  }

  void h () {
    System.out.println ("A.h ()"); 
  }
}


class B extends A {
  int f (int x) {
    System.out.format ("B.f (%d)%n", x);
    return super.f (x);
  }

  int g () { 
    System.out.println ("B.g ()"); 
    A r = this;
    r.h ();
    return 0;
  }

  void h () {
    System.out.println ("B.h ()"); 
    super.h ();
  }
}


public class Inheritance08 {
  public static void main (String[] args) {
    A x = new B (); 
    x.f (3);
  }
}

Example 3

Consider the following Java program. Work out what will be printed when it runs. Verify your answer by compiling and running the program (yes, it is meant to do that).

class A {
  int f (int x) {
    System.out.format ("A.f (%d)%n", x);
    return (x == 0) ? g () : f (x - 1); 
  }

  int g () {
    System.out.println ("A.g ()"); 
    return 0;
  }

  void h () {
    System.out.println ("A.h ()"); 
  }
}


class B extends A {
  int f (int x) {
    System.out.format ("B.f (%d)%n", x);
    return this.f (x);
  }

  int g () { 
    System.out.println ("B.g ()"); 
    A r = this;
    r.h ();
    return 0;
  }

  void h () {
    System.out.println ("B.h ()"); 
    super.h ();
  }
}


public class Inheritance09 {
  public static void main (String[] args) {
    A x = new B (); 
    x.f (3);
  }
}

Composition And Delegation

The Java programs in this section use composition and delegation.

Example 1

Consider the following Java program. Work out what will be printed when it runs.

Verify your answer by compiling and running the program.

class A {
  int f (int x) {
    System.out.format ("A.f (%d)%n", x);
    if (x == 0) {
      return g ();
    } else {
      return f (x - 1);
    }
  }

  int g () {
    System.out.println ("A.g ()");
    return 0;
  }
}

class B {
  A a = new A ();

  int f (int x) {
    System.out.format ("B.f (%d)%n", x);
    return a.f (x);
  }

  int g () {
    System.out.println ("B.g ()");
    return 0;
  }
}

public class Inheritance05 {
  public static void main (String[] args) {
    new B ().f (3);
  }
}

Example 2

Consider the following Java program. Work out what will be printed when it runs.

Verify your answer by compiling and running the program.

interface I {
  int f (int x);
  int g ();
}

class A implements I {
  I back = this;

  public int f (int x) {
    System.out.format ("A.f (%d)%n", x);
    if (x == 0) {
      return back.g ();
    } else {
      return back.f (x - 1);
    }
  }

  public int g () {
    System.out.println ("A.g ()");
    return 0;
  }
}

class B implements I {
  A a;

  B () {
    a = new A ();
    a.back = this;
  }

  public int f (int x) {
    System.out.format ("B.f (%d)%n", x);
    return a.f (x);
  }

  public int g () {
    System.out.println ("B.g ()");
    return 0;
  }
}

public class Inheritance06 {
  public static void main (String[] args) {
    new B ().f (3);
  }
}

Static And Dynamic Dispatch In C++

The C++ programs in this section contrast static/dynamic dispatch.

Example 1

Consider the following C++ program. Work out what will be printed when it runs.

Verify your answer by compiling and running the program.

#include <iostream>

class A { 
public: 
  void foo () { std::cout << "A" << std::endl; } 
};

class B : public A {
public: 
  void foo () { std::cout << "B" << std::endl; } 
};

int main () { 
  std::cout << "sizeof (A) = " << sizeof (A) << std::endl;
  std::cout << "sizeof (B) = " << sizeof (B) << std::endl;
  A* x = new B ();
  x->foo (); 
}

Example 2

Consider the following C++ program. Work out what will be printed when it runs.

Verify your answer by compiling and running the program.

#include <iostream>

class A { 
public: 
  virtual void foo () { std::cout << "A" << std::endl; } 
};

class B : public A {
public: 
  void foo () override { std::cout << "B" << std::endl; } 
};

int main () { 
  std::cout << "sizeof (A) = " << sizeof (A) << std::endl;
  std::cout << "sizeof (B) = " << sizeof (B) << std::endl;
  A* x = new B ();
  x->foo (); 
}

JS Prototype Usage

Set Prototype Property

Run the following JavaScript in your browser’s console (Firefox and Chrome recommeded). In particular, convince yourself that the two counters have different values for n, but that they both have a next property.

function Counter () {
  this.n = 0;
}

Counter.prototype.next  = function () { 
  return this.n++; 
}

var c1 = new Counter ();
var c2 = new Counter ();

c1.next ();
c1.next ();
c2.next ();

Translate Java Class to JavaScript

Translate the following Java class to a JavaScript function (intended to be used with constructor context) with an appropriate prototype.

class Point {
  int x;
  int y;
  Point (int x, int y) {
    this.x = x;
    this.y = y;
  }
  void move (int x2, int y2) {
    x = x + x2;
    y = y + y2;
  }
}

Chained Prototype Property

Consider the following Java code that uses subclassing. The class B inherits (the implementation of) f from class A.

  class A {
    A () {
    }

    void f () {
      System.out.println ("A.f ()");
    }
  }

  class B extends A {
    B () {
      super (); // Call to constructor of superclass A
    }

    void g () {
      System.out.println ("B.g ()");
    }
  }

The following JavaScript code shows how to convert the implementation inheritance in Java above into JavaScript’s prototype-based inheritance. (JavaScript is dynamically typed, so interface inheritance is not relevant in JavaScript).

The key is the use of Object.create. It is used to set the B.prototype property to a new object with a prototype of A.prototype. Consequently, if we lookup a property on an ‘instance’ of B, we search for a property of that name in the following order:

  1. In the ‘instance’ itself.

  2. In the B.prototype object.

  3. In the A.prototype object.

Try this out by running the following JavaScript in your browser’s console (Firefox and Chrome recommeded).

function A () {
}

A.prototype.f  = function () { 
  console.log ("A.f ()");
}

function B () {
  A.call (this); // Call to constructor A
}

B.prototype ` Object.create (A.prototype); // Creates a new object with `A.prototype= as its prototype

B.prototype.g  = function () { 
  console.log ("B.g ()");
}

var r = new A ();
var s = new B ();

r.f ();
r.g ();
s.f ();
s.g ();

function getProps (obj) {
  var props = [];
  for (var f in obj) { 
    props.push (f); 
  }
  return props;
}

function getProps2 (obj) {
  var props = [];
  for (var f in obj) { 
    if (obj.hasOwnProperty (f)) { props.push (f); }
  }
  return props;
}

getProps (r);  // Print out properties for r (including properties in the prototype chain)
getProps2 (r); // Print out properties for r (excluding the prototype chain)
getProps (s);  // Print out properties for s (including properties in the prototype chain)
getProps2 (s); // Print out properties for s (excluding the prototype chain)

Simulating OOP With Function Pointers In C

A number of large C programs/libraries emulate dynamic dispatch from object-oriented PLs using function pointers. In addition, there are well thought out frameworks, e.g., http:_/ldeniau.web.cern.ch_ldeniau/cos.html, for object-oriented programming in C. These approaches rely upon the programmer being very systematic, and the C compiler will not catch bugs that arise from not following the programming pattern correctly.

To give a flavor of these approaches, we now consider how to emulate simple object-oriented programming in C using function pointers.

We will translate the following Java program to C.

interface Language {
  String greet (String name);
}

class English implements Language {
  public String greet (String name) { return "Hello " + name; }
}

class French implements Language {
  private int count = 0;
  public String greet (String name) { this.count++; return "Bonjour " + name; }
  public int getCount ()            { return this.count; }
}

public class SimpleOOP {
  public static void main (String[] args) {
    Language[] langs = new Language[] { new English (), new French (), };
    String name = System.console ().readLine ("What is your name? ");
    for (Language lang : langs) {
      System.out.println (lang.greet (name));
    }
    System.out.format ("French count = %d%n", ((French) langs[1]).getCount ());
  }
}

When run, the Java program reads a name from the console, and prints “hello” with the name in both English and French. The important part is that their is an interface with a greet method, and that interface is implemented in two different classes.

$ javac SimpleOOP.java 

$ java SimpleOOP
What is your name? alice
Hello alice
Bonjour alice
French count = 1

We can translate the Java program into the C program below with structures describing object instances and their vtables. Warning: for the sake of brevity, the array/string handling is unsafe in this program!

This C program is missing the code to actually invoke the greet and get_count methods. Read through the C program and fill in that code. Then compile and run your program.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct language_vtable;
struct language_inst;
struct english_vtable;
struct english_inst;
struct french_vtable;
struct french_inst;

/* ------------------------------------------------------------ */

struct language_vtable {
  char *(*greet) (struct language_inst *this, char *name);
};

struct language_inst {
  struct language_vtable *vtable;
};

/* ------------------------------------------------------------ */

struct english_vtable {
  char *(*greet) (struct english_inst *this, char *name);
};

struct english_inst {
  struct english_vtable *vtable;
};

char *english_greet (struct english_inst *this, char *name) {
  char *buffer = (char *) malloc (strlen (name));
  strcpy (buffer, "Hello ");
  strcat (buffer, name);
  return buffer;
};

struct english_vtable unique_english_vtable;

void init_unique_english_vtable () {
  unique_english_vtable.greet = &english_greet;
}

struct english_inst *new_english_inst () {
  struct english_inst *result = (struct english_inst *) malloc (sizeof (struct language_inst));
  result->vtable = &unique_english_vtable;
  return result;
}

/* ------------------------------------------------------------ */

struct french_vtable {
  char *(*greet) (struct french_inst *this, char *name);
  int (*get_count) (struct french_inst *this);
};

struct french_inst {
  struct french_vtable *vtable;
  int count;
};

char *french_greet (struct french_inst *this, char *name) {
  this->count++;
  char *buffer = (char *) malloc (strlen (name));
  strcpy (buffer, "Bonjour ");
  strcat (buffer, name);
  return buffer;
};

int french_get_count (struct french_inst *this) {
  return this->count;
};

struct french_vtable unique_french_vtable;

void init_unique_french_vtable () {
  unique_french_vtable.greet = &french_greet;
  unique_french_vtable.get_count = &french_get_count;
}

struct french_inst *new_french_inst () {
  struct french_inst *result = (struct french_inst *) malloc (sizeof (struct language_inst));
  result->vtable = &unique_french_vtable;
  return result;
}

/* ------------------------------------------------------------ */

void strip_new_line (char *buffer) {
  while (*buffer != 0 && *buffer != '\n') {
    buffer++;
  }
  if (*buffer == '\n') {
    *buffer = 0;
  }
}

/* ------------------------------------------------------------ */

int main (void) {
  init_unique_english_vtable ();
  init_unique_french_vtable ();

  struct language_inst *(langs[2]);
  langs[0] = (struct language_inst *) new_english_inst ();
  langs[1] = (struct language_inst *) new_french_inst ();

  printf ("What is your name? ");
  char name_buffer[1024];
  fgets (name_buffer, 1024, stdin);
  strip_new_line (name_buffer);
  for (int i = 0; i < 2; i++) {
    char *res = /* TODO: invoke the 'greet' method of langs[i] */;
    puts (res);
    free (res);
  }
  
  /* TODO: invoke the 'get_count' method of langs[1], then print the result */
  
  return 0;
}

Solutions

Solution: Simulating OOP With Function Pointers In C

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct language_vtable;
struct language_inst;
struct english_vtable;
struct english_inst;
struct french_vtable;
struct french_inst;

/* ------------------------------------------------------------ */

struct language_vtable {
  char *(*greet) (struct language_inst *this, char *name);
};

struct language_inst {
  struct language_vtable *vtable;
};

/* ------------------------------------------------------------ */

struct english_vtable {
  char *(*greet) (struct english_inst *this, char *name);
};

struct english_inst {
  struct english_vtable *vtable;
};

char *english_greet (struct english_inst *this, char *name) {
  char *buffer = (char *) malloc (strlen (name));
  strcpy (buffer, "Hello ");
  strcat (buffer, name);
  return buffer;
};

struct english_vtable unique_english_vtable;

void init_unique_english_vtable () {
  unique_english_vtable.greet = &english_greet;
}

struct english_inst *new_english_inst () {
  struct english_inst *result = (struct english_inst *) malloc (sizeof (struct language_inst));
  result->vtable = &unique_english_vtable;
  return result;
}

/* ------------------------------------------------------------ */

struct french_vtable {
  char *(*greet) (struct french_inst *this, char *name);
  int (*get_count) (struct french_inst *this);
};

struct french_inst {
  struct french_vtable *vtable;
  int count;
};

char *french_greet (struct french_inst *this, char *name) {
  this->count++;
  char *buffer = (char *) malloc (strlen (name));
  strcpy (buffer, "Bonjour ");
  strcat (buffer, name);
  return buffer;
};

int french_get_count (struct french_inst *this) {
  return this->count;
};

struct french_vtable unique_french_vtable;

void init_unique_french_vtable () {
  unique_french_vtable.greet = &french_greet;
  unique_french_vtable.get_count = &french_get_count;
}

struct french_inst *new_french_inst () {
  struct french_inst *result = (struct french_inst *) malloc (sizeof (struct language_inst));
  result->vtable = &unique_french_vtable;
  return result;
}

/* ------------------------------------------------------------ */

void strip_new_line (char *buffer) {
  while (*buffer != 0 && *buffer != '\n') {
    buffer++;
  }
  if (*buffer == '\n') {
    *buffer = 0;
  }
}

/* ------------------------------------------------------------ */

int main (void) {
  init_unique_english_vtable ();
  init_unique_french_vtable ();

  struct language_inst *(langs[2]);
  langs[0] = (struct language_inst *) new_english_inst ();
  langs[1] = (struct language_inst *) new_french_inst ();

  printf ("What is your name? ");
  char name_buffer[1024];
  fgets (name_buffer, 1024, stdin);
  strip_new_line (name_buffer);
  for (int i = 0; i < 2; i++) {
    char *res = (*(langs[i]->vtable->greet)) (langs[i], name_buffer);
    puts (res);
    free (res);
  }
  
  if (langs[1]->vtable != (struct language_vtable *) &unique_french_vtable) {
    fprintf (stderr, "Cannot safely cast to french_inst\n");
    exit (1);
  }
  struct french_inst *french = (struct french_inst *) langs[1];
  int count = (*(french->vtable->get_count)) (french);
  printf ("French count = %d\n", count);
  
  return 0;
}
$ gcc -std=c99 -o simple-oop-solution simple-oop-solution.c

$ ./simple-oop-solution 
What is your name? alice
Hello alice
Bonjour alice
French count = 1