5 6
g; g;
13 14
private: Window
char *label ; Menu
// :::
g;
class Menu inherits data and methods from
both Window and Screen
{ i.e., sizeof (Menu) >= sizeof (Window) >= sizeof
(Screen) Screen/Window/Menu hierarchy
15 16
Variations on a Screen ::: Using the Screen Hierarchy
e.g.,
ps1 : ps2 : class Screen f public: virtual void dump (ostream &); = 0 g
Screen Screen
class Window : public Screen f
public: virtual void dump (ostream &);
g;
class Menu : public Window f
public: virtual void dump (ostream &);
g;
// stand-alone function
w: void dump image (Screen *s, ostream &o) f
Window
// Some processing omitted
Menu s->dump (o);
// (*s->vptr[1]) (s, o));
g
Screen s; Window w; Menu m;
Bit Vector bv;
A pointer to a derived class can be as- // OK: Window is a kind of Screen
signed to a pointer to any of its public dump image (&w, cout);
base classes without requiring an explicit // OK: Menu is a kind of Screen
cast: dump image (&m, cout);
// OK: argument types match exactly
dump image (&s, cout);
Menu m; Window &w = m; Screen *ps1 = &w; // Error: Bit Vector is not a kind of Screen!
Screen *ps2 = &m; dump image (&bv, cout);
17 18
g;
const char *person ;
19 20
Implementation and Use-case Alternatives to Specialization
Note that we could also use object com-
Birthday::print could print the person's name position instead of inheritance for this ex-
as well as the date, e.g., ample, e.g.,
void Birthday::print (ostream &s) const f class Birthday f
s << this->person << " was born on "; public
Date::print (s); Birthday (char *n, int m, int d, int y):
s << "\n"; date (m, d, y), person (n) fg
g // same as before
private:
e.g., Date date ;
char *person ;
g;
const Date july 4th (7, 4, 1993);
Birthday my birthday ("Douglas C. Schmidt", 7, 18, 1962);
However, in this case we would not be able
july 4th.print (cerr); to utilize the dynamic binding facilities for
// july 4th, 1993 base classes and derived classes
my birthday.print (cout); { e.g.,
// Douglas C. Schmidt was born on july 18th, 1962
Date *dp = &my birthday; Date *dp = &my birthday;
dp->print (cerr); // ERROR, Birthday is not a subclass of date!
// ??? what gets printed ??? { While this does not necessarily aect reusabil-
// (*dp->vptr[1])(dp, cerr); ity, it does aect extensibility
:::
21 22
Extension/Generalization
Using Inheritance for Example
Extension/Generalization
Using class Vector as a private base class
Derived classes add state variables and/or for derived class Stack
operations to the properties and opera-
tions associated with the base class class Stack : private Vector f /* ::: */ g;
{ Note, the interface is generally widened!
In this case, Vector's operator[] may be
{ Data member and method access privileges may reused as an implementation for the Stack
push and pop methods
also be modied
{ Note that using private inheritance ensures that
operator[] does not show up in the interface
Extension/generalization is often used to for class Stack!
faciliate reuse of implementations, rather
than interface
{ However, it is not always necessary or correct Often, a better approach in this case is
to export interfaces from a base class to de- to use a composition/Has-A rather than
rived classes
a descendant/Is-A relationship :::
23 24
Vector Interface
Using class Vector as a base class for a
Vector Implementation
derived class such as class Checked Vector
or class Ada Vector e.g.,
{ One can dene a Vector class that implements
an unchecked, uninitialized array of elements template <class T>
of type T Vector<T>::Vector (size t s): size (s), buf (new T[s]) fg
template <class T>
e.g., /* File Vector.h (incomplete wrt ini- Vector<T>::~Vector (void) f delete [] this->buf ; g
tialization and assignment) */
template <class T> size t
// Bare-bones implementation, fast but not safe Vector<T>::size (void) const f return this->size ; g
template <class T>
class Vector f template <class T> T &
public: Vector<T>::operator[] (size t i) f return this->buf [i]; g
Vector (size t s);
~Vector (void); int main (void) f
size t size (void) const; Vector<int> v (10);
T &operator[] (size t index); v[6] = v[5] + 4; // oops, no initial values
int i = v[v.size ()]; // oops, out of range!
private: // destructor automatically called
T *buf ; g
size t size ;
g;
25 26
f g
if (this->in range (i)) catch (RANGE ERROR)
return (*(inherited *) this)[i]; f /* */ g
// return BASE::operator[](i);
:::
else g
throw RANGE ERROR (i);
g
29 30
Design Tip
Note, dealing with parent and base classes Ada Vector Interface
{ It is often useful to write derived classes that
do not encode the names of their direct parent The following is an Ada Vector example,
class or base class in any of the method bodies where we can have array bounds start at
{ Here's one way to do this systematically: something other than zero
class Base f
public: /* File ada vector.h (still incomplete wrt
int foo (void); initialization and assignment .) */ :::
g;
class Derived 1 : public Base f #include "vector.h"
typedef Base inherited; // Ada Vectors are also range checked!
public: template <class T>
int foo (void) f inherited::foo (); g class Ada Vector : private Checked Vector<T> f
g; public:
class Derived 2 : public Derived 1 f Ada Vector (size t l, size t h);
typedef Derived 1 inherited;
public: T &operator ()(size t i) throw (RANGE ERROR)
int foo (void) f inherited::size; // explicitly extend visibility
inherited::foo (); private:
g typedef Checked Vector<T> inherited;
g; size t lo bnd ;
{ This scheme obviously doesn't work as trans- g;
parently for multiple inheritance:::
31 32
Ada Vector Use-case
Example Ada Vector Usage (File main.C)
Ada Vector Implementation #include <iostream.h>
#include <stdlib.h>
e.g., class Ada Vector (cont'd) #include "ada vector.h"
Memory Layout
Memory layouts in derived classes are cre- Base Class Constructor
ated by concatenating memory from the
base class(es)
{ e.g., // from the cfront-generated .c le Constructors are called from the \bottom
up"
struct Vector f
T *buf 6Vector;
size t size 6Vector; Destructors are called from the \top down"
g;
struct Checked Vector f
T *buf 6Vector;
size t size 6Vector; e.g.,
g;
struct Ada Vector f /* Vector constructor */
T *buf 6Vector; // Vector struct Vector *
size t size 6Vector; // part ct 6VectorFi (struct Vector * 0this, size t 0s) f
size t lo bnd 10Ada Vector; // Ada Vector if ( 0this jj ( 0this =
g; nw FUi (sizeof (struct Vector))))
(( 0this->size 6Vector = 0s),
The derived class constructor calls the base ( 0this->buf 6Vector =
constructor in the \base initialization sec- nw FUi ((sizeof (int)) * 0s)));
tion," i.e., return 0this;
g
Ada Vector<T>::Ada Vector (size t lo, size t hi)
: inherited (hi , lo + 1), lo bnd (lo) fg
35 36
Derived Class Constructors
Destructor
e.g.,
/* Checked Vector constructor */ Note, destructors, constructors, and as-
struct Checked Vector * ct 14Checked VectorFi ( signment operators are not inherited
struct Checked Vector * 0this, size t 0s) f
if ( 0this jj ( 0this =
nw FUi (sizeof (struct Checked Vector)))) However, they may be called automati-
0this = ct 6VectorFi ( 0this, 0s); cally were necessary, e.g.,
return 0this;
g char dt 6VectorFv (
/* Ada Vector constructor */ struct Vector * 0this, int 0 free) f
struct Ada Vector * ct 10Ada VectorFiT1 ( if ( 0this) f
struct Ada Vector * 0this, size t 0lo, size t 0hi) f dl FPv ((char *) 0this->buf 6Vector);
if ( 0this jj ( 0this = if ( 0this)
nw FUi (sizeof (struct Ada Vector)))) if ( 0 free & 1)
if ((( 0this = ct 14Checked VectorFi ( 0this, dl FPv ((char *) 0this);
0hi , 0lo + 1)))) g
0this->lo bnd 10Ada Vector = 0lo; g
return 0this;
g
37 38
41 42
43 44
Private Derivation Protected Derivation
e.g., e.g.,
class A f class A f
public: public:
<public A> <public A>
private: protected:
<private A> <protected A>
protected: private:
<protected A> <private A>
g; g;
class B : private A f // also class B : A class B : protected A f
public: public:
<public B> <public B>
protected: protected:
<protected B> [protected A]
private: [public A]
[public A] <protected B>
[protected A] private:
<private B> <private B>
g; g;
45 46
B E +-----------+-----+-----+-----+ private:
E S | private | n/a | n/a | n/a | int p ;
R S +-----------+-----+-----+-----+
p p p g;
u r r
b o i
class B : private A f
l t v
public:
A::f; // Make public
protected:
A::g ; // Make protected
Note that the resulting access is always g;
the most restrictive of the two
47 48
Common Errors with Access
Control Speciers
General Rules for Access Control
It is an error to \increase" the access of
an inherited method in a derived class Speciers
{ e.g., you may not say:
class B : private A f Private methods of the base class are not
// nor protected nor public! accessible to a derived class (unless the
public: derived class is a friend of the base class)
A::p ; // ERROR!
g;
If the subclass is derived publically then:
It is also an error to derive publically and
then try to selectively decrease the visibil- 1. Public methods of the base class are accessible
ity of base class methods in the derived to the derived class
class
2. Protected methods of the base class are acces-
{ e.g., you may not say: sible to derived classes and friends only
class B : public A f
private:
A::f; // ERROR!
g;
49 50
Caveats
Using protected methods weakens the data
hiding mechanism since changes to the Overview of Multiple Inheritance
base class implementation might aect all
derived classes. e.g.,
in C++
class Vector f
public:
// C++ allows multiple inheritance
protected:
:::
// allow derived classes direct access { i.e., a class can be simultaneously derived from
T *buf ; two or more base classes
size t size ;
g;
class Ada Vector : public Vector f { e.g.,
public:
T &operator[] (size t i) f class X f /* . */ g;
return this->buf [i];
:::
class Y : public X f /* . */ g;
g
:::
51 52
Multiple Inheritance Illustrated Liabilities of Multiple Inheritance
A base class may legally appear only once
in a derivation list, e.g.,
Base
NON-VIRTUAL Base { class Two Vector : public Vector, public Vec-
tor // ERROR!
INHERITANCE
Vector
NON-VIRTUAL Vector Initializing Virtual Base Classes
INHERITANCE
With C++ you must chose one of two
methods to make constructors work cor-
rectly for virtual base classes:
Checked Init Checked
Vector Checked Vector 1. You need to either supply a constructor in a
Vector virtual base class that takes no arguments (or
has default arguments), e.g.,
Vector::Vector (size t size = 100); // has problems :::
Vector 2. Or, you must make sure the most derived class
calls the constructor for the virtual base class
in its base initialization section, e.g.,
v v
VIRTUAL Init Checked Vector (size t size, const T &init):
INHERITANCE Vector (size), Check Vector (size),
Init Vector (size, init)
Checked Init Checked
Vector Checked Vector
Vector
57 58
59 60
Init Checked Vector Interface and
Checked Vector Interface Driver
A simple multiple inheritance example that
A simple extension to the Vector base class provides for both an initialized and range
that provides range checked subscripting checked Vector
template <class T> template
classpublic <class T>
Init Checked Vector :
class Checked Vector : public virtual Vector<T> Checked Vector<T>, public Init Vector<T> f
f publicInit
:
Checked Vector (size t size, const T &init):
public: Vector<T> (size),
Checked Vector (size t size): Vector<T> (size) fg Init Vector<T> (size, init),
T &operator[] (size t i) throw (RANGE ERROR) f Checked Vector<T> (size) fg
// Inherits Checked Vector::operator[]
if (this->in range (i)) g;
return (*(inherited *) this)[i]; Driver program
else throw RANGE ERROR (i);
g int main
// Inherits inherited::size. try f(int argc, char *argv[]) f
private: size t size = ::atoi (argv[1]);
size t init = ::atoi (argv[2]);
typedef Vector<T> inherited; Init Checked Vector<int> v (size, init);
cout << "vector size = " << v.size ()
<< ", vector contents = ";
bool in range (size t i) const f
return i < this->size (); for (size t i = 0; i < v.size (); i++)
cout << v[i];
g
g; cout << "\n" << ++v[v.size () , 1] << "\n";
catch (RANGE ERROR)
f /* */ g
g
:::
61 62
g;
63 64