UMBC CMSC202, Computer Science II, Fall 1998,
Sections 0101, 0102, 0103, 0104
20. Abstract Base Classes & Polymorphism
Thursday November 12, 1998
[Previous Lecture]
[Next Lecture]
Assigned Reading: 9.5-9.9
Handouts (available on-line):
Programs from this lecture:
Topics Covered:
- In this lecture, we wrap up some loose ends and technicalities
about C++ classes and rules for derivations.
- Constant member functions. A member function is called a constant
member function if it doesn't alter the host object.
- Next, we resolve some questions about overriding base class member
functions in a derived class. In a simple header file we declare a base class called
Base, a publicly derived class PubDer and a privately derived class called
PrivDer. The Base class has two member functions named foo with
different signatures. (Aside: you cannot have a member function and a data
member with the same name.)
- The first program and sample run shows that we can invoke the
two member functions foo() from a Base object without difficulty.
- In the publicly derived class PubDer, we have a member function called
foo that has a different signature from the either of the Base
member functions called foo. Question: using a PubDer object
P, what does P.foo() mean? The second program and sample run shows that
P.foo() only refers to the foo() in the derived
class, because this function overrides all functions in the Base
class called foo --- not just the functions with the same
signature. Note that we can still access the Base foo functions
using P.Base::foo().
- In the privately derived class PrivDer, we expose the
identifier foo from the base class by including in the
public section of the declaration:
Base::foo ; // expose Base class identifier foo
(Note the lack of () after foo.) This declaration exposes both
member functions called foo as demonstrated by the third program and sample run.
- More rules for private derivation. We have been saying that a
derived class and a base class are compatible. In particular, a pointer
to a derived object can be assigned to a pointer to a base object without
explicit type casting. For private derivations, the rules are a bit
more complicated. In a private derivation, the base class is not
visible to the client. If we allow the client to convert a derived
class pointer to a base class pointer, then the client would gain access
to the base class members. Thus, this pointer conversion is only
allowed in parts of a program that already have access to the base
class (e.g., from a member function or a friend of the derived class).
- A simple declaration for a private
derivation of a class called PrivDer from a base class called Base.
- The first program and sample run show the normal usage of
functions foo1() and foo2() that take Base&
and Base * parameters, respectively.
- The second program and sample run show that calling
foo1() and foo2() from the main program using a PrivDer
object results in a compile-time error. The compiler is complaining about
converting a PrivDer reference or pointer to a Base reference or pointer
from a section of the program that does not have access to the Base class.
- The third program and sample run show that converting a
PrivDer reference or pointer to a Base reference or pointer is legal from
pdmem(), a member function of PrivDer, and from buddy(),
a friend of PrivDer.
- Run-time type information. In the
previous lecture in the ListCell
class, we us the member function id() to return the address
of a static member. This is so we can figure out the type of an object
at run time. The typeinfo.h library provides similar
functionality with a cleaner interface. Unfortunately, this utility is
not supported by all compilers.
- We reuse the Person, Student and
Faculty classes from Lecture 18.
- The first program and
sample run show a simple case
of "dynamic binding". The function invoked by ptr->id()
is determined at run time, not compile time, because id()
is a virtual function.
- The second program and
sample run demonstrate the
use of the typeid() function from typeinfo.h.
We are able to print out the name of the type of the object that
ptr points to. We can also compare the type of the object
with a name of a class.
- The third program and
sample run show that when
the function typer() is compiled separately, the
typeid() function doesn't quite work. The same program
runs correctly on the GL system, so this might simply be an
indication that CC on everest was installed incorrectly. Also, g++
does not know about typeinfo.h.
- Generic Quicksort. In the examples of generic functions we've
seen so far, we've used Selection Sort or Bubble Sort for simplicity. We
can create a generic Quicksort program using virtual functions. In many
approaches, the generic Quicksort program would require complicated class
declarations, extra levels of indirections (sorting pointers to objects) or
lots of duplicated code. Another approach is to use the C++ template
facility.
- A straightforward implementation of the Quicksort algorithm: quicksort.C. Note that the
macro DATA is defined as an int in the header file: quicksort.h. If we change
DATA to be float we would get a QuickSort()
function that sorts an array of float instead of int.
- The first program and sample run demonstrate the simple use of
QuickSort() to sort an array of int.
- We modify the file containing QuickSort() so it does not read
in the header file:
quicksort2.C. Then, from the second main program we can define the macro
DATA twice and #include the quicksort2.C file
twice. This gives us two overloaded functions called Quicksort().
One that sorts an array of int, the other sorts an array of
float. The sample run shows
that the compiler gives us a warning about redefining DATA,
but otherwise the program runs correctly.
- The previous example is a "silly hack". The #include
preprocessor command was not intended to be used this way.
However, this example illustrates how the C++ template facility
works in principle --- i.e., we reuse code that is textually
identical except for the type. The proper way to do this in C++ is
to create a template file for
QuickSort(). The template file is then included in
the main program that uses
QuickSort(). Whenever the compiler encounters the need for
a QuickSort() function of a new type, it uses the template
to compile such a function. In this way, we can write a single
template for QuickSort() and have it work for arrays of
int, float and even BString. In the case
of BString, it is important that the BString class has the
comparison operators defined. See sample run.
[Previous Lecture]
[Next Lecture]
Last Modified:
22 Jul 2024 11:28:49 EDT
by
Richard Chang
Back up
to Fall 1998 CMSC 202 Section Homepage