CPSC 427a: Object-Oriented Programming
Michael J. Fischer
STL and Polymorphism
Derivation
from
STL
containers
Common wisdom on the internet says not to inherit from STL
containers.
For example, http://en.wikipedia.org/wiki/Standard_Template_Library says, “STL containers are not intended to be used as base classes (their destructors are deliberately non-virtual); deriving from a container is a common mistake.”
This reflects Rule 35 of Sutter and Alexandrescu, “Avoid inheriting from classes that were not designed to be base classes.”
Replacing authority with understanding
C++ is a complicated and powerful language.
Some constructs such as classes are used for several different
purposes.
What is appropriate in one context may not be in another.
Simple rules will not make you a good C++ programmer. Thought, understanding, and experience will.
Two kinds of derivation
C++ supports two distinct kinds of derivation:
We say B is derived from A, and B inherits members from A.
Each B object has an A object embedded within it.
The derivation is simple if no members of A are virtual; otherwise it is polymorphic.
How are they the same?
With both kinds of derivation, a function of the base class A can be overridden by a function in B.
In both cases, one can create and delete objects of class B.
Both A’s and B’s destructor are called when a B object is deleted.
Output: | B’s destructor called |
A’s destructor called |
What is simple derivation good for?
Some uses for simple derivation.
With simple derivation, the derived class is the public interface.
Often protected or private derivation is used to hide the base class from the users of the derived class.
What are the problems with simple derivation?
What is polymorphic derivation good for?
Some uses for polymorphic derivation.
What are the problems of polymorphic derivation?
Every polymorphic base class (containing even one virtual function) adds
a runtime type tag to each instance.
This costs in both time and space.
Contrasts between simple and polymorphic derivation
Simple derivation:
Polymorphic derivation:
Containment as an alternative to simple derivation
Often the same class can be implemented using either containment or
derivation.
Derivation:
A’s public member functions are inherited by B.
Containment:
Access to A’s public member functions requires a “pass-through” function for delegation.
Argument for containment
Containment is a more distant relationship than derivation.
Less coupling between classes is safer and less error-prone.
Using containment, derived class is explicit about what is exported.
For more info, see http://www.gotw.ca/publications/mill06.htm.
STL container as a base class
We apply these concepts to STL base classes.
Base classes are simple, not polymorphic (no virtual functions, no virtual
destructor).
This means that they should only be used with simple derivation. They
are not suitable as base classes for polymorphic derivation.
Often containment is preferable, but the large number of member functions they support makes it cumbersome to get the same degree of functionality in the derived class as comes “for free” with derivation.
Can I turn an STL container into a polymorphic base class?
Yes, sort of. Here’s the idea.
A polymorphic base class
MyVectorInt is a polymorphic base class with virtual destructor and can be used as such.
Dynamic cast
It is always okay to cast a pointer to a derived class into a pointer to the
base class, as in the previous example.
The reverse is only semantically meaningful if the allocated type of the object actually is the type to which it is being cast. In that case, one can use a dynamic_cast to effect the converstion.
dynamic_cast returns NULL if p is pointing to something that does not have dynamic type Derived*.
Exceptions
Exceptions
An exception is an event that prevents normal continuation.
Exceptions may be due to program errors or data errors, but they may also be due to external events:
How to respond to different kinds of exceptions is application-dependent.
Exception handling
When an exception occurs, a program has several options:
Problem: Exceptions are often detected at a low level of the code.
Knowledge of how to respond resides at a higher level.
C-style solution using status returns
The C library functions generally report exceptions by returning status
values or error codes.
Advantages: How to handle exception is delegated to the caller.
Disadvantages:
C++ exception mechanism
C++ exception mechanism is a means for a low-level routine to report an
exception directly to a higher-level routine.
This separates exception-handling code from normal processing
code.
An exception is reported using the keyword throw.
An exception is handled in a catch block.
Each routine in the chain between the reporter and the handler is exited cleanly, with all destructors called as expected.