| CS 330 | Home | Schedule | Resources |
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 CounterNote the presence of the nesting marker "$", which turns a statement into an expression with the value of the variable at $. The expression
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
{MyCounter
get($)} is equivalent to local X in {MyCounter get(X)} X end
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]
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 CollectionNote 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:
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
X={C1 isEmpty($)} or
{C1 isEmpty(X)}.
Add a method union(C) to this class. The call {C1 union(C2)} should union the collections C1 and C2. After the call, C1 contains the union of C1 and C2 and C2 is empty. (Unlike a true set, we won't throw out duplicates in our union.)
Now define a class SortedCollection.
The interface of a sorted collection is the same as a collection, with
the difference that the method get(X) now returns the elements in the
order of sorting. In other words, the method always returns the
smallest element of the collection. Your class must inherit from the
class Collection.
Use your class SortedCollection to sort a list. Does your implementation correspond to one of the sorting algorithms seen previously? Which one?
Can you easily convert an unspecified collection into a sorted collection?
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:
#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;
}
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.
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.