CPSC 427: Object-Oriented Programming
Michael J. Fischer
Preview
Lecture
15
October
24,
2018
Polymorphic Derivation
Some uses for derived classes.
Type Hierarchies
Consider following simple type hierarchy:
We have a base class B and derived classes U and V.
A different method f() is defined in each.
Relationships: A U is a B (and more). A V is a B (and more).
A U can be used wherever a B is expected.
Example: Definition f(B& x) ... ; call U z; f(z);
Inside of f(), only the B-part of z is visible. This is called slicing.
Pointers and slicing
Declare B* bp; U* up = new U; V* vp = new V.
Can write bp = up; or bp = vp;.
Why does this make sense?
If bp = up, then bp points to the embedded B-instance of object *up. The rest of *up is inaccessible because of object slicing.
Ordinary derivation
In our previous example
bp can point to objects of type B, type U, or type V.
Want bp->f() to refer to U::f() if bp points to a U object.
Want bp->f() to refer to V::f() if bp points to a V object.
However, with ordinary derivation, bp->f() always refers to B::f().
Polymorphic derivation
The keyword virtual allows for polymorphic derivation.
A virtual function is dispatched at run time to the class of the actual
object.
bp->f() refers to U::f() if bp points to a U.
bp->f() refers to V::f() if bp points to a V.
bp->f() refers to B::f() if bp points to a B.
Here, the type refers to the allocation type.
Unions and type tags
We can regard bp as a pointer to the union of types B, U and
V.
To know which of B::f(), U::f() or V::f() to use for the call
bp->f() requires runtime type tags.
If a class has virtual functions, the compiler adds a type tag field to each object.
This takes space at run time.
The compiler also generates a vtable to use in dispatching calls on virtual functions.
Virtual destructors
Consider delete bp;, where bp points to a U but has type B*.
The U destructor will not be called unless destructor B::~B() is declared
to be virtual.
Note: The base class destructor is always called, whether or not it is
virtual.
In this way, destructors are different from other member methods.
Conclusion: If a derived class has a non-empty destructor, the base class destructor should be declared virtual.
Uses of Polymorphism
Uses of polymorphism Some uses of polymorphism:
Multiple representations
Might want different representations for an object.
Example: A point in the plane can be represented by either Cartesian or
Polar coordinates.
A Point base class can provide abstract operations on points.
E.g., virtual int quadrant() const returns the quadrant of
*this.
For Cartesian coordinates, quadrant is determined by the signs of the x and y coordinates of the point.
For polar coordinates, quadrant is determined by the angle θ.
Both Cartesian and Polar derived classes should contain a method for int quadrant() const.
Heterogeneous containers
One might wish to have a stack of Point objects.
The element type of the stack would be Point*.
The actual values would have type either Cartesian* or Polar*.
The automatically generated type tags and dynamic dispatching obviates
the need to cast the result of pop() to the correct type.
Example:
Stack st; Point* p; |
p = st.pop(); | // no need to cast result |
p->quadrant(); | // automatic dispatch |
Uses of polymorphism: Run-time variability
Two types are closely related; differ only slightly.
Example: Company has several different kinds of employees.
Pure virtual functions
Suppose we don’t want B::f() and we never create instances of the base class B.
Rather, we want every derived class to provide a definition for f().
We make B::f() into a pure virtual function by writing =0.
A pure virtual function is sometimes called a promise.
It tells the compiler that a construct like bp->f() is legal.
The compiler requires every derived class to contain a method f().
Abstract classes
An abstract class is a class with one or more pure virtual functions.
An abstract class cannot be instantiated. It can only be used as the base
for another class.
The destructor can never be a pure virtual function but will generally be
virtual.
A pure abstract class is one where all member functions are pure virtual
(except for the destructor) and there are no data members,
Pure abstract classes define an interface à la Java.
An interface allows user-supplied code to integrate into a large system.
Introduction to the
C++ Standard Library
A bit of history C++ standardization.
The standard library was derived from several different sources.
STL (Standard Template Library) portion of the C++ standard was derived from an earlier STL produced by Silicon Graphics (SGI).
Some useful classes
Here are some useful classes, some of which you have already seen:
Class stringstream
A stringstream object (in the default case) acts like an ostream
object.
It can be used just like you would use cout.
The characters go into an internal buffer rather than to a file or
device.
The buffer can be retrieved as a string using the str() member function.
stringstream example
Example: Creating a label from an integer.