Title: Case Studies and Examples in Object Oriented Programming
1Case Studies and Examples in Object Oriented
Programming
2Virtual Copy
- Problem Given a reference to an object, create a
duplicate object. - the constructor will not work!
- Base b // some object
- Base x new Base(b) // Nope. makes a base
- Derived y new Base(b) // Nope, illegal
- Derived z new
- Derived(dynamic_castltDerivedgt(b)) // YUCK!
- Even the last option fails unless the programmer
knows the actual type of the object.
3Virtual Copy
- A constructor cannot be virtual, but we can have
a virtual function that acts a lot like a
constructor - class Base
- Base copy(void) const 0 // make a duplicate
-
- Every object implements a trivial copy
- class Derived public Base
- Base copy(void) const
- return new Derived(this)
4Smart Pointers
- Note that the virtual copy function must return a
pointer into the heap. - How do we ensure that this object is eventually
reclaimed? - Use garbage collector
- Use smart pointers.
- The C standard includes an auto_ptr that will
automatically delete objects.
5auto_ptr semantics
- When the auto_ptr goes out of scope, the object
it points to is automatically destroyed - No two auto_ptrs should point at the same object!
- The assignment operation sets the right hand side
to nil. - The only way to make two auto_ptrs point at the
same object is to construct both of them from the
same base pointer.
6template ltclass Tgt class auto_ptr T
ptr public explicit auto_ptr(T p 0)
ptr(p) auto_ptr(auto_ptr a)
ptr(a.release()) auto_ptr
operator(auto_ptr a) if (a ! this)
delete ptr ptr a.release()
return this auto_ptr() delete ptr
T release() T tmp ptr ptr 0
return tmp
7Wrappers
- A Wrapper is similar (in implementation) to a
smart pointer. However, a does not look like a
pointer. - A wrapper has member functions for each of the
methods of the wrapped object. - Users of the wrapper do not even need to know
that an inheritance hierachy exists.
8Using Wrappers to Morph Shapes
- Consider an inheritance hierarchy with _Shape as
the abstract base, and draw(), copy() and morph()
as virtual members. - both copy() and morph() return newly allocated
_Shape objects from the heap. - class Shape
- _Shape shape
- public
- void draw(void) const shape-gtdraw()
- Shape(const Shape other)
- shape other.copy()
-
9More with the Shape Wrapper
- void morph(void)
- _Shape t shape-gtmorph()
- delete shape
- shape t
-
- The Wrapper has a real copy constructor (and
should have an assignment operator)
10Object Oriented Creation
- In an object oriented design are goals are
- Implement the application with knowledge of only
the base class. - Implement the subclasses independently of each
other. - How can we create objects and still maintain
these two objectives?
11Factory Method
- A Factory method is a function that creates a new
object. Lets assume that the information we
need to determine the type of object to be
created comes from the input stream - Base my_factory(void)
- cin gtgt type
- switch (type)
- case type_1 return new Derived1()
- case type_2 return new Derived2()
12Using Prototypes
- What if we have a hash table filled with
representative objects? - Base my_factory(void)
- cin gtgt type
- Base prototype table.lookup(type)
- return prototype-gtcopy()
-
- Much closer to our objectives, the factory can be
written with no knowledge of the derived types.
13Using Functions
- The drawback of the prototype pattern is that
each object must be an exact copy of some
original. What if there are more parameters (in
the input stream?) that need to be used to
construct the new object? - Instead of putting a representative object into
the hash table, put a function. - This function can read the params from the input
and create the appropriate object.
14Initialization
- Our factory method will only work correctly if
the hash table is initialized before the first
use. - ideally, each subtype would install its own
entry into the hash table! - Accomplishing this feat reliably in C involves
creating initialization objects with static
linkage (and a few other tricks).
15Returning References to static locals
- class Base
- static HashTable hash_table(void)
- static HashTable ht
- return ht
-
- public
- static void insert_table(Key k, Function f)
- hash_table().insert(k, f)
-
-
16Static Linkage for Initializers
- Place this code in Derived.h
- class Derived_Initializer
- public
- Derived_Initializer(void)
- static bool inserted_already false
- if (inserted_already) return
- Baseinsert_table(Derived_Key, Derived_Fun)
- inserted_already true
-
-
- static Derived_Initializer __derived_initializer
17Simulation
- Object oriented programming was invented to
support simulation. - The first object oriented language was Simula,
a language designed to support simulation. - Well discuss event-driven simulation.
18Events
- Nothing interesting happens between events.
- All computation takes place as a result of
processing events. - Events are application-specific actions that take
place in the simulated world. - For example, if we were simulating a network
switch the events could be - packets arriving at the switch
- packets departing the switch.
19Simulation Time
- Events are processed in the order that they occur
in the simulation world. - Every event has a time stamp. The time is
simulation time (not real time). - When an event is created, its time stamp must be
some time in the future. - Simulation time advances in jumps as events are
processed. - Events are kept in a priority queue, sorted by
time stamp. - The rate at which simulation time advances is
determined solely by the time stamps in the
events.
20Selecting Your Events
- Selecting the events for your simulation is the
most important (and sometimes most challenging)
part of designing a simulation. - Everything interesting must be an event
- Some not-quite interesting events may be
processed too - must err on the safe side, not to miss anything.
21Simulation State
- The second most important design decision for a
simulation is determining how much information to
keep in the simulation state. - The more information, the slower the simulation
will run. - This problem is mitigated by updating only part
of the state at each event. As events are
processed, some of the state information may be
stale!
22Object-Oriented State
- The simulation state is a natural application of
object oriented programming. - There is often natural correspondence between the
data structures and physical objects. - The ability to use OO polymorphism helps simplify
the program structure.
23The LifeForm
- In our example, the simulation state will be
represented by the state of the LifeForm objects. - LifeForm is an abstract base class with the
following fields - pos the last known position
- course the last known direction it was traveling
- speed the last known speed at which it was
moving - energy how strong the life form is.
- plus a few other fields useful for the simulation.
24The Craig Sub Type
- The actual life forms in our simulation will be
concrete sub types of LifeForm, for example
Craig. - A Craig object inherits the position, energy,
etc. fields, but cannot access these fields
directly. - No subtype of LifeForm can ever be allowed to
determine its absolute position! - There will be some simulation events defined by
the LifeForm base class - and some events defined by the subtypes.
25A Representative Event
- Assume that a Craig life form will change
direction at time 10. - We need to write a method that will be called
when this event occurs Craigturn(). - We need to create an event that will cause this
method to be invoked on the correct object at the
correct moment in simulation time.
26The Turn Method for Craig
- void Craigturn(void) // note no params
- double course get_course()
- course course M_PI / 2 // turn left
- set_course(course)
-
- get_course and set_course are methods inherited
from the base class. If implemented correctly,
the object has now turned 90 degrees to the left. - We have several choices for making event objects
that will invoke this function
27A Pure OOP Style
- class LifeFormEvent
- double timestamp
- public
- virtual void doit(void) 0
-
- class CraigTurnEvent public LifeFormEvent
- Craig obj
- public
- void doit(void) obj-gtturn()
-
- Now we just need to create a CraigTurnEvent with
the appropriate time stamp and pointer to the
correct Craig object and insert this event into
the event queue.
28Comments on OOP Events
- Its a bit tedious to create these event objects.
- For every event we have to write an entire class,
with constructors, etc. - C at least allows the classes to be defined
with scope local to a function. - This will help us avoid name clashes, since
everyone will probably create an event called
MyEvent.
29Pointers to Functions
- Since we had to write the function Craigturn
anyway, why not just stick a pointer to that
function in the event and avoid having to create
a whole new Event subclass? - Whoops, Craigturn is a member function, so the
event will still need an object to invoke this
function. - Still this will be easier to create.
30Comments on Pointers
- From a performance standpoint, the
pointers-to-member functions does not really hurt
us. - Virtual functions are implemented using pointers
to functions. - In theory, using the pointer-to-member could
allow us to avoid using the virtual doit()
function in the Event Base class. - BUT A Pointer to a member function cannot point
to a member function in a derived class!
31A Combined Approach With Templates
- The advantage of the pointer-to-function approach
was that it was less trouble to create a new
event. - The problem was the type of the pointer.
- Aha! use templates.
- Well create a base class Event, and have a
template class as a subtype.
32Canceling Events
- It is frequently the case that the processing of
an event defines new events for the future. - Occasionally, the processing of an event results
in a future event no longer being relevant. - How can we stop these events?
- The LifeForm event class can be deleted. When an
event is deleted it is automatically removed from
the event queue. - Be sure you only delete events that have not yet
occurred!
33LifeForm Movement
- In our simulation, we dead reckon the objects
position. - Each time we update the pos field, we also record
the time stamp. - At any future time, we can calculate the objects
position by subtracting the time of the last
update from the current time, multiplying this by
the speed and adding the distance traveled to the
objects current position.
34Boundary Crossings
- LifeForms may eventually encounter (physically
collide with) other LifeForms. - We know that this event cannot happen unless at
least one of the two LifeForms crosses a boundary
in the QuadTree. - Therefore, we should check for a possible
collision each time an object crosses a boundary.
35Scheduling the Boundary Crossing Event
- When an object is inserted into the QuadTree, the
object is placed into a rectangular region. - We can ask the QuadTree what is the distance
(scalar) that the object must travel along some
defined course in order to leave its current
region. - Knowing this distance, we can calculate when the
object will leave its region. - Please fudge the time by a small amount (increase
by a factor of 1.001). This trick will avoid
problems with floating point roundoff.
36Using the QuadTree
- You must not change the position variable while
an object is in the QuadTree. - You can lose the object this way!
- Instead, you may
- remove the object from the tree, update the pos
field, and then re-insert the object. - invoke the update_position function on the
QuadTree (and then change the pos field). - Do not insert two objects at the same position.
- You can ask the QuadTree if a position
is_occupied.
37QuadTree Callbacks
- We check for collisions when objects cross region
boundaries. - Event is scheduled to occur at this time.
- What happens when new objects are inserted and
the region gets smaller? - We may cross the border and collide with the new
object BEFORE the event!!! - The QuadTree will invoke a callback on any object
whos region has changed size since the object
was last inserted. - The callback must be inserted along with the
object.
38Changing Course
- When a LifeForm changes course, we should update
the position!