CS 330 Home Schedule Resources

Assignment 14
Objects, Classes and Polymorphism

Estimated time: 5.5 hours

In this assignment, we will define objects based on classes, and will highlight the concept of polymorphism. The example below shows class syntax, and object creation and use.

   declare
   class Counter
attr value
meth init % (re)initialize the counter value:=0
end
meth inc % increment the counter value:=@value+1
end meth get(X) % return the current value of the counter by binding X X=@value
end end MyCounter={New Counter init} for X in [65 81 92 34 70] do {MyCounter inc} end {Browse {MyCounter get($)}} % displays 5
Note the presence of the nesting marker "$", which turns a statement into an expression with the value of the variable at $. The expression {MyCounter get($)} is equivalent to
   local X in {MyCounter get(X)} X end

Exercises

  1. Sequences.  A Sequence is a collection of elements in a given order. An object implementing a sequence must have a method getElements(L) which binds L to the list of elements in the sequence.

    Stacks and queues are two types of sequences. Define the classes Stack and Queue using the techniques of explicit-state programming. Your classes must be sequences, i.e. they must implement the method getElements(L). Examples of utilization:

       declare
       S={New Stack init}
       Q={New Queue init}
    
       {S push(a)}
       {S push(b)}
       {Q enqueue(a)}
       {Q enqueue(b)}
    
       {Browse {S getElements($)}}   % shows [b a]
       {Browse {Q getElements($)}}   % shows [a b]
        

  2. Collections. A collection is a (multi-)set of values. Here is a simple class which implements collections. Three methods are defined: put(X), get(X) and isEmpty(B).

       class Collection
    attr elements
    meth init % initializes the collection elements:=nil
    end
    meth put(X) % insert X into the collection elements:=X|@elements
    end meth get($) % extracts an element and returns it case @elements of X|Xr then elements:=Xr X end end meth isEmpty($) % returns true if the collection is empty @elements==nil
    end end
    Note that by default, the body of a method is a statement. But the nesting marker $ allows us to define methods as functions. Their contents must be an expression to which the nesting marker will be bound. In the example, the method put(X) is defined as a statement, while the method isEmpty($) is defined as an expression. The syntax of method invocations remains the same: X={C1 isEmpty($)} or {C1 isEmpty(X)}.

  3. Inheritance in C++
    Part A - (20 minutes) Write a program modeled after this pseudocode example to demonstrate that C++ uses static inheritance for variables.

    Part B - (20 minutes) Write a second program modeled after the following pseudocode example to illustrate that C++ uses static inheritance for methods if you do not use the virtual keyword.

      class c1 extends object
        method initialize () 1
        method m1 () 1
        method m2 () send self m1() 
      class c2 extends c1
        method m1 () 2
      let o1 = new c1 ()
          o2 = new c2 ()
      in makelist(send o1 m1(),
                  send o2 m1(),
                  send 02 m2() )
    
    Part C - (20 minutes) Write a third program, using the virtual keyword but otherwise identical to your second program, to demonstrate that C++ uses dynamic method inheritance for methods that are declared as virtual. The pseudocode examples store intermediate results and return them as a list, but you don't have to do this--just use normal C++ output as needed to print out what the different methods return. In your submitted file, include the following for each of these three programs:

  4. Class member scope
    Which object/function calls must be commented out for the following C++ code to run successfully? When it runs, what is the output? Explain your results.
    #include < iostream>
    using namespace std;
    
    class A {
    public: void Fpub() { cout << "public" << endl;}
    private: void Fpri() { cout << "private" << endl;}
    protected: void Fpro() { cout << "protected" << endl;}
    };
    
    class A1: protected A {
    public: 
      void F1(A1& b) {
        cout << "hereA1" << endl;
        Fpub();
        Fpro();
        Fpri();
        cout << "hereA2" << endl;
        b.Fpub();
        b.Fpro();
        b.Fpri();
      }
    };
      int main(){
      A1 a;
      A1 b;
      a.Fpub();
      a.Fpri();
      a.Fpro();
      a.F1(b);
      return 0;
    }
    

  5. Exercises 1 and 2 in the textbook (page 567). [Replicated here for your convenience.]

    1. Uninitialized objects. The function New creates a new object when given a class and an initial message. Write another function New2 that does not require an initial message. That is, the call Obj={New2 Class} creates a new object without initializing it. Hint: write New2 in terms of New.
    2. Protected methods in the Java sense. A protected method in Java has two parts: it is accessible throughout the package that defines the class and also by descendants of the class. For this exercise, define a linguistic abstraction that allows annotating a method or attribute as protected in the Java sense. Show how to encode this in the model of section 7.3.3 by using name values. Use functors to represent Java packages. For example, one approach might be to define the name value globally in the functor and also tostore it in an attribute called setOfAllProtectedAttributes. Since the attribute is inherited, the method name is visible to all subclasses. Work out the details of this approach.

  6. Feedback.