• No results found

The C++ Programming Language Single and Multiple Inheritance in C++ Douglas C. Schmidt www.cs.wustl.eduschmidt [email protected] Washington University, St. Louis 

N/A
N/A
Protected

Academic year: 2021

Share "The C++ Programming Language Single and Multiple Inheritance in C++ Douglas C. Schmidt www.cs.wustl.eduschmidt [email protected] Washington University, St. Louis "

Copied!
64
0
0

Loading.... (view fulltext now)

Full text

(1)

The C++ Programming Language

Single and Multiple Inheritance in C++

Douglas C. Schmidt

www.cs.wustl.edu/



schmidt/

[email protected]

Washington University, St. Louis

(2)

Background



Object-oriented programming is often de- ned as the combination of Abstract Data Types (ADTs) with Inheritance and Dy- namic Binding



Each concept addresses a di erent aspect of system decomposition:

1. ADTs decompose systems into two-dimensional grids of modules

{

Each module has public and private inter- faces

2. Inheritance decomposes systems into three-dimensional hierarchies of modules

{

Inheritance relationships form a \lattice"

3. Dynamic binding enhances inheritance

{

e.g., defer implementation decisions until late in the design phase or even until run-time!

(3)

Data Abstraction vs. Inheritance

DATA ABSTRACTION (2 DIMENTIONAL)

DATA ABSTRACTION (2 DIMENTIONAL)

INHERITANCE (3 DIMENTIONAL)

(4)

Motivation for Inheritance



Inheritance allows you to write code to handle certain cases and allows other de- velopers to write code that handles more specialized cases, while your code contin- ues to work



Inheritance partitions a system architec- ture into semi-disjoint components that are related hierarchically



Therefore, we may be able to modify and/or reuse sections of the inheritance hierarchy without disturbing existing code, e.g.,

{

Change sibling subtree interfaces

 i.e., a consequence of inheritance

{

Change implementation of ancestors

 i.e., a consequence of data abstraction

(5)

Inheritance Overview



A type (called a subclass or derived type) can inherit the characteristics of another type(s) (called a superclass or base type)

{

The term subclass is equivalent to derived type



A derived type acts just like the base type, except for an explicit list of:

1. Specializations

{

Change implementations without changing the base class interface

 Most useful when combined with dynamic binding

2. Generalizations/Extensions

{

Add new operations or data to derived classes

(6)

Visualizing Inheritance

Base Base Derived

Derived 11

Derived Derived

33

Derived Derived

44

Derived Derived

22 Derived

Derived 55

Derived Derived

66

(7)

Types of Inheritance



Inheritance comes in two forms, depend- ing on number of parents a subclass has

1. Single Inheritance (SI)

{

Only one parent per derived class

{

Form an inheritance \tree"

{

SI requires a small amount of run-time over- head when used with dynamic binding

{

e.g., Smalltalk, Simula, Object Pascal 2. Multiple Inheritance (MI)

{

More than one parent per derived class

{

Forms an inheritance \Directed Acyclic Graph"

(DAG)

{

Compared with SI, MI adds additional run- time overhead (also involving dynamic bind- ing)

{

e.g., C++, Ei el, Flavors (a LISP dialect)

(8)

Inheritance Trees vs. Inheritance DAGs

Derived Derived

33 Derived Derived

11

Base Base

Derived Derived

44 INHERITANCE

INHERITANCE TREE

TREE

Derived Derived

22

Derived Derived

33 Base

Base11 DerivedDerived 11

Derived Derived

44 INHERITANCE

INHERITANCE DAG

DAG

Base Base22

(9)

Inheritance Bene ts

1. Increase reuse and software quality

 Programmers reuse the base classes instead of writing new classes

{

Integrates black-box and white-box reuse by allowing extensibility and modi cation with- out changing existing code

 Using well-tested base classes helps reduce bugs in applications that use them

 Reduce object code size

2. Enhance extensibility and comprehensibil- ity

 Helps support more exible and extensible ar- chitectures (along with dynamic binding)

{

i.e., supports the open/closed principle

 Often useful for modeling and classifying hierarchically- related domains

(10)

Inheritance Liabilities

1. May create deep and/or wide hierarchies that are hard to understand and navigate without class browser tools

2. May decrease performance slightly

 i.e., when combined with multiple inheritance and dynamic binding

3. Without dynamic binding, inheritance has only limited utility

 Likewise, dynamic binding is almost totally use- less without inheritance

4. Brittle hierarchies, which may impose de-

pendencies upon ancestor names

(11)

Inheritance in C++



Deriving a class involves an extension to the C++ class declaration syntax



The class head is modi ed to allow a deriva- tion list consisting of base classes



e.g.,

class

Foo

f

/*

:::g

;

class

Bar :

public

Foo f /* :::g;

class

Foo :

public

Foo,

public

Bar f /* :::g;

(12)

Key Properties of C++

Inheritance



The base/derived class relationship is ex- plicitly recognized in C++ by prede ned standard conversions

{

i.e., a pointer to a derived class may always be assigned to a pointer to a base class that was inherited publically

 But not vice versa:::



When combined with dynamic binding, this special relationship between inherited class types promotes a type-secure, polymor- phic style of programming

{

i.e., the programmer need not know the actual type of a class at compile-time

{

Note, C++ is not truly polymorphic

 i.e., operations are not applicable to objects that don't contain de nitions of these op- erations at some point in their inheritance hierarchy

(13)

Simple Screen Class



The following code is used as the base class:

class

Screen f

public

:

Screen (

int

= 8,

int

= 40,

char

= ' ');

~Screen (

void

);

short

height (

void

)

const

f

return this

->height ; g

short

width (

void

)

const

f

return this

->width ; g

void

height (

short

h) f

this

->height = h; g

void

width (

short

w) f

this

->width = w; g Screen &forward (

void

);

Screen &up (

void

);

Screen &down (

void

);

Screen &home (

void

);

Screen &bottom (

void

);

Screen &display (

void

);

Screen &copy (

const

Screen &);

// :::

private

:

short

height , width ;

char

*screen , *cur pos ;

g;

(14)

Subclassing from Screen

 class Screen

can be a public base class of

class Window



e.g.,

class

Window :

public

Screen f

public

:

Window (

const

Point &,

int

rows = 24,

int

columns = 80,

char

default char = ' ');

void

set foreground color (Color &);

void

set background color (Color &);

void

resize (

int

height,

int

width);

// :::

private

:

Point center ;

Color foreground ; Color background ; // :::

g;

(15)

Multiple Levels of Derivation



A derived class can itself form the basis for further derivation, e.g.,

class

Menu :

public

Window f

public

:

void

set label (

const char

*l);

Menu (

const

Point &,

int

rows = 24,

int

columns = 80,

char

default char = ' ');

// :::

private

:

char

*label ; // :::

g;

 class

both

WindowMenu

inherits data and methods from and

Screen

{

i.e.,

sizeof

(Menu) >=

sizeof

(Window) >=

sizeof

(Screen)

(16)

The Screen Inheritance Hierarchy

Window Window Screen

Screen

Menu Menu Point

Point

Color Color



Screen/Window/Menu hierarchy

(17)

Variations on a Screen

: : :

w : Windoww : Window

ps1 : ps1 : Screen

Screen ps2 :ps2 : Screen Screen

Menu Menu



A pointer to a derived class can be as- signed to a pointer to any of its public base classes without requiring an explicit cast:

Menu m; Window &w = m; Screen *ps1 = &w;

Screen *ps2 &m;

(18)

Using the Screen Hierarchy



e.g.,

class

Screen f

public

:

virtual void

dump (ostream &); = 0 g

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

void

dump image (Screen *s, ostream &o) f // Some processing omitted

s->dump (o);

// (*s->vptr[1]) (s, o));

g

Screen s; Window w; Menu m;

Bit Vector bv;

// OK: Window is a kind of Screen dump image (&w, cout);

// OK: Menu is a kind of Screen dump image (&m, cout);

// OK: argument types match exactly dump image (&s, cout);

// Error: Bit Vector is not a kind of Screen!

dump image (&bv, cout);

(19)

Using Inheritance for Specialization



A derived class specializes a base class by adding new, more speci c state variables and methods

{

Method use the same interface, even though they are implemented di erently

 i.e., \overridden"

{

Note, there is an important distinction between overriding, hiding, and overloading:::



A variant of this is used in the template method pattern

{

i.e., behavior of the base class relies on func- tionality supplied by the derived class

{

This is directly supported in C++ via abstract base classes and pure virtual functions

(20)

Specialization Example



Inheritance may be used to obtain the fea- tures of one data type in another closely related data type



For example, class Date represents an ar- bitrary Date:

class

Date f

public

Date (

virtual void

//: :::

int

m,print (ostream &s)

int

d,

int

y);

const

;

private int

: month , day , year ;

g;



Class Birthday derives from Date, adding a name eld representing the person's birth- day, e.g.,

class

Birthday :

public

Date f

public

Birthday (: : Date (m, d, y), person (strdup (n))

const char

*n,

int

m,

int

d,

int

y)fg

~Birthday (

void

) f free (person ); g

virtual void

print (ostream &s)

const

;

// :::

private const char

: *person ;

g;

(21)

Implementation and Use-case



Birthday::print could print the person's name as well as the date, e.g.,

void

Birthday::print (ostream &s)

const

f

s <<

this

->person << " was born on ";

Date::print (s);

s << "\n";

g



e.g.,

const

Date july 4th (7, 4, 1993);

Birthday my birthday ("Douglas C. Schmidt", 7, 18, 1962);

july 4th.print (cerr);

// july 4th, 1993

my birthday.print (cout);

// Douglas C. Schmidt was born on july 18th, 1962 Date *dp = &my birthday;

dp->print (cerr);

// ??? what gets printed ???

// (*dp->vptr[1])(dp, cerr);

(22)

Alternatives to Specialization



Note that we could also use object com- position instead of inheritance for this ex- ample, e.g.,

class

Birthday f

public

Birthday (

char

*n,

int

m,

int

d,

int

y):

date (m, d, y), person (n) fg // same as before

private

:

Date date ;

char

*person ;

g;



However, in this case we would not be able to utilize the dynamic binding facilities for base classes and derived classes

{

e.g.,

Date *dp = &my birthday;

// ERROR, Birthday is not a subclass of date!

{

While this does not necessarily a ect reusabil- ity, it does a ect extensibility:::

(23)

Using Inheritance for Extension/Generalization



Derived classes add state variables and/or operations to the properties and opera- tions associated with the base class

{

Note, the interface is generally widened!

{

Data member and method access privileges may also be modi ed



Extension/generalization is often used to faciliate reuse of implementations, rather than interface

{

However, it is not always necessary or correct to export interfaces from a base class to de- rived classes

(24)

Extension/Generalization Example



Using for derived

classclassVectorStack

as a private base class

class

Stack :

private

Vector f /* :::*/ g;



In this case, Vector's reused as an implementation for the Stack

operator[]

may be

push

and

pop

methods

{

Note that using private inheritance ensures that

operator[] does not show up in the interface for class Stack!



Often, a better approach in this case is

to use a composition/Has-A rather than

a descendant/Is-A relationship

:::

(25)

Vector Interface



Using derived class such as class or class

classAda VectorVector

as a base class for a

Checked Vector

{

One can de ne a Vector class that implements an unchecked, uninitialized array of elements of type T



e.g., /* File Vector.h (incomplete wrt ini- tialization and assignment) */

// Bare-bones implementation, fast but not safe

template

<

class

T>

class

Vector f

public

:

Vector (size t s);

~Vector (

void

);

size t size (

void

)

const

;

T &

operator

[] (size t index);

private

:

T *buf ; size t size ;

g;

(26)

Vector Implementation



e.g.,

template

<

class

T>

Vector<T>::Vector (size t s): size (s), buf (

new

T[s]) fg

template

<

class

T>

Vector<T>::~Vector (

void

) f

delete

[]

this

->buf ; g

template

<

class

T> size t

Vector<T>::size (

void

)

const

f

return this

->size ; g

template

<

class

T> T &

Vector<T>::

operator

[] (size t i) f

return this

->buf [i]; g

int

main (

void

) f

Vector<

int

> v (10);

v[6] = v[5] + 4; // oops, no initial values

int

i = v[v.size ()]; // oops, out of range!

// destructor automatically called

g

(27)

Bene ts of Inheritance



Inheritance enables modi cation and/or ex- tension of ADTs without changing the orig- inal source code

{

e.g., someone may want a variation on the ba- sic Vector abstraction:

1. A vector whose bounds are checked on every reference

2. Allow vectors to have lower bounds other than 0

3. Other vector variants are possible too:::

 e.g., automatically-resizing vectors, initial- ized vectors, etc.



This is done by de ning new derived classes that inherit the characteristics of the base class

Vector

{

Note that inheritance also allows code to be shared

(28)

Checked Vector Interface



The following is a subclass of Vector that allows run-time range checking:



/* File Checked-Vector.h (incomplete wrt initialization and assignment) */

struct

RANGE ERROR f

"

range error

" (size t index);

// :::

g

;

template

<

class

T>

class

Checked Vector :

public

Vector<T> f

public

:

Checked Vector (size t s);

T &

operator

[] (size t i)

throw

(RANGE ERROR);

// Vector::size () inherited from base class Vector.

protected

:

bool

in range (size t i)

const

;

private

:

typedef

Vector<T> inherited;

g;

(29)

Implementation of Checked Vector



e.g.,

template

<

class

T>

bool

Checked Vector<T>::in range (size t i)

const

f

return

i <

this

->size ();

g

template

<

class

T>

Checked Vector<T>::Checked Vector (size t s) : inherited (s) fg

template

<

class

T> T &

Checked Vector<T>::

operator

[] (size t i)

throw

(RANGE ERROR)

f

if

(

this

->in range (i))

return

(*(inherited *)

this

)[i];

//

return

BASE::

operator

[](i);

else throw

RANGE ERROR (i);

g

(30)

Checked Vector Use-case



e.g.,

#

include

"Checked Vector.h"

typedef

Checked Vector<

int

> CV INT;

int

foo (

int

size)

f

try

f CV INT cv (size);

int

i = cv[cv.size ()]; // Error detected!

// exception raised::: // Call base class destructor

g

catch

(RANGE ERROR)

f /* :::*/ g

g

(31)

Design Tip



Note, dealing with parent and base classes

{

It is often useful to write derived classes that do not encode the names of their direct parent class or base class in any of the method bodies

{

Here's one way to do this systematically:

class

Base f

public

:

int

foo (

void

);

g;

class

Derived 1 :

public

Base f

typedef

Base inherited;

public

:

int

foo (

void

) f inherited::foo (); g

g;

class

Derived 2 :

public

Derived 1 f

typedef

Derived 1 inherited;

public

:

int

foo (

void

) f

inherited::foo ();

g

g;

{

This scheme obviously doesn't work as trans- parently for multiple inheritance:::

(32)

Ada Vector Interface



The following is an Ada Vector example, where we can have array bounds start at something other than zero



/* File ada vector.h (still incomplete wrt initialization and assignment

:::

.) */

#

include

"vector.h"

// Ada Vectors are also range checked!

template

<

class

T>

class

Ada Vector :

private

Checked Vector<T> f

public

:

Ada Vector (size t l, size t h);

T &

operator

()(size t i)

throw

(RANGE ERROR) inherited::size; // explicitly extend visibility

private

:

typedef

Checked Vector<T> inherited;

size t lo bnd ;

g;

(33)

Ada Vector Implementation



e.g., class Ada Vector (cont'd)

template

<

class

T>

Ada Vector<T>::Ada Vector (size t lo, size t hi) : inherited (hi , lo + 1), lo bnd (lo) fg

template

<

class

T> T &

Ada Vector<T>::

operator

()(size t i)

throw

(RANGE ERROR) f

if

(

this

->in range (i , this->lo bnd ))

return

Vector<T>::

operator

[] (i ,

this

->lo bnd );

// or Vector<T> &self = *(Vector<T> *) this;

// self[i ,

this

->lo bnd ];

else throw

RANGE ERROR (i);

g

(34)

Ada Vector Use-case



Example Ada Vector Usage (File main.C)

#

include

<iostream.h>

#

include

<stdlib.h>

#

include

"ada vector.h"

int

main (

int

argc,

char

*argv[]) f

try

f

size t lower = ::atoi (argv[1]);

size t upper = ::atoi (argv[2]);

Ada Vector<

int

> ada vec (lower, upper);

ada vec (lower) = 0;

for

(size t i = lower + 1; i <= ada vec.size (); i++) ada vec (i) = ada vec (i , 1) + 1;

// Run-time error, index out of range ada vec (upper + 1) = 100;

// Vector destructor called when // ada vec goes out of scope

g

catch

(RANGE ERROR) f /* :::*/ g

g

(35)

Memory Layout



Memory layouts in derived classes are cre- ated by concatenating memory from the base class(es)

{

e.g., // from the cfront-generated .c le

struct

Vector f

T *buf 6Vector;

size t size 6Vector;

g;

struct

Checked Vector f T *buf 6Vector;

size t size 6Vector;

g;

struct

Ada Vector f

T *buf 6Vector; // Vector size t size 6Vector; // part

size t lo bnd 10Ada Vector; // Ada Vector

g;



The derived class constructor calls the base constructor in the \base initialization sec- tion," i.e.,

Ada Vector<T>::Ada Vector (size t lo, size t hi) : inherited (hi lo + 1), lo bnd (lo)

(36)

Base Class Constructor



Constructors are called from the \bottom up"



Destructors are called from the \top down"



e.g.,

/* Vector constructor */

struct

Vector *

ct 6VectorFi (

struct

Vector * 0this, size t 0s) f

if

( 0this jj ( 0this =

nw FUi (

sizeof

(

struct

Vector))))

(( 0this->size 6Vector = 0s), ( 0this->buf 6Vector =

nw FUi ((

sizeof

(

int

)) * 0s)));

return

0this;

g

(37)

Derived Class Constructors



e.g.,

/* Checked Vector constructor */

struct

Checked Vector * ct 14Checked VectorFi (

struct

Checked Vector * 0this, size t 0s) f

if

( 0this jj ( 0this =

nw FUi (

sizeof

(

struct

Checked Vector)))) 0this = ct 6VectorFi ( 0this, 0s);

return

0this;

g/* Ada Vector constructor */

struct

Ada Vector * ct 10Ada VectorFiT1 (

struct

Ada Vector * 0this, size t 0lo, size t 0hi) f

if

( 0this jj ( 0this =

nw FUi (

sizeof

(

struct

Ada Vector))))

if

((( 0this = ct 14Checked VectorFi ( 0this, 0hi , 0lo + 1))))

0this->lo bnd 10Ada Vector = 0lo;

return

0this;

g

(38)

Destructor



Note, destructors, constructors, and as- signment operators are not inherited



However, they may be called automati- cally were necessary, e.g.,

char

dt 6VectorFv (

struct

Vector * 0this,

int

0 free) f

if

( 0this) f

dl FPv ((

char

*) 0this->buf 6Vector);

if

( 0this)

if

( 0 free & 1)

dl FPv ((char *) 0this);

g

g

(39)

Describing Relationships Between Classes



Consumer/Composition/Aggregation

{

A class is a consumer of another class when it makes use of the other class's services, as de ned in its interface

 For example, a Stack implementation could rely on an array for its implementation and thus be a consumer of the Array class

{

Consumers are used to describe a Has-A rela- tionship



Descendant/Inheritance/Specialization

{

A class is a descendant of one or more other classes when it is designed as an extension or specialization of these classes. This is the no- tion of inheritance

{

Descendants are used to describe an Is-A rela- tionship

(40)

Has-A vs. Is-A Relationships

CONSUMER RELATIONSHIP

DESCENDANT RELATIONSHIP

Stack

Vector

Checked Vector Vector

VectorAda

(41)

Interface vs. Implementation Inheritance



Class inheritance can be used in two pri- mary ways:

1. Interface inheritance: a method of creating a subtype of an existing class for purposes of set- ting up dynamic binding, e.g.,

{

Circle is a subclass of Shape (i.e., Is-A rela- tion)

{

A Birthday is a subclass of Date

2. Implementation inheritance: a method of reusing an implementation to create a new class type

{

e.g., a

class

Stack that inherits from

class

Vector. A Stack is not really a subtype or specialization of Vector

{

In this case, inheritance makes implementa- tion easier, since there is no need to rewrite and debug existing code.

 This is called \using inheritance for reuse"

 i.e., a pseudo-Has-A relation

(42)

The Dangers of Implementation Inheritance



Using inheritance for reuse may sometimes be a dangerous misuse of the technique

{

Operations that are valid for the base type may not apply to the derived type at all

 e.g., performing an subscript operation on a stack is a meaningless and potentially harm- ful operation

class

Stack :

public

Vector f // :::

g;

Stack s;

s[10] = 20; // could be big trouble!

{

In C++, the use of a

private

base class mini- mizes the dangers

 i.e., if a class is derived \private," it is illegal to assign the address of a derived object to a pointer to a base object

{

On the other hand, a consumer/Has-A relation might be more appropriate:::

(43)

Private vs Public vs Protected Derivation



Access control speci ers (i.e.,

public

,

pri-

vate

,

protected

) are also meaningful in

the context of inheritance



In the following examples:

{

<:::.> represents actual (omitted) code

{

[:::.] is implicit



Note, all the examples work for both data

members and methods

(44)

Public Derivation



e.g.,

class

A f

public

:

<public A>

protected

:

<protected A>

private

:

<private A>

g;

class

B :

public

A f

public

:

[public A]

<public B>

protected

:

[protected A]

<protected B>

private

:

<private B>

g;

(45)

Private Derivation



e.g.,

class

A f

public

:

<public A>

private

:

<private A>

protected

:

<protected A>

g;

class

B :

private

A f // also

class

B : A

public

:

<public B>

protected

:

<protected B>

private

:

[public A]

[protected A]

<private B>

g;

(46)

Protected Derivation



e.g.,

class

A f

public

:

<public A>

protected

:

<protected A>

private

:

<private A>

g;

class

B :

protected

A f

public

:

<public B>

protected

:

[protected A]

[public A]

<protected B>

private

:

<private B>

g;

(47)

Summary of Access Rights



The following table describes the access rights of inherited methods

{

The vertical axis represents the access rights of the methods of base class

{

The horizontal access represents the mode of inheritance

INHERITANCE

ACCESS

+---+---+---+---+

M A | public | pub | pro | pri |

E C +---+---+---+---+

M C | protected | pro | pro | pri |

B E +---+---+---+---+

E S | private | n/a | n/a | n/a |

R S +---+---+---+---+

p p p

u r r

b o i

l t v



Note that the resulting access is always

the most restrictive of the two

(48)

Other Uses of Access Control Speci ers



Selectively rede ne visibility of individual methods from base classes that are de- rived privately

class

A f

public

:

int

f ();

int

g ;

:::

private

:

int

p ;

g;

class

B :

private

A f

public

:

A::f; // Make public

protected

:

A::g ; // Make protected

g;

(49)

Common Errors with Access Control Speci ers



It is an error to \increase" the access of an inherited method in a derived class

{

e.g., you may not say:

class

B :

private

A f

// nor

protected

nor

public

!

public

:

A::p ; // ERROR!

g;



It is also an error to derive publically and then try to selectively decrease the visibil- ity of base class methods in the derived class

{

e.g., you may not say:

class

B :

public

A f

private

:

A::f; // ERROR!

g;

(50)

General Rules for Access Control Speci ers



Private methods of the base class are not accessible to a derived class (unless the derived class is a

friend

of the base class)



If the subclass is derived publically then:

1. Public methods of the base class are accessible to the derived class

2. Protected methods of the base class are acces- sible to derived classes and friends only

(51)

Caveats



Using protected methods weakens the data hiding mechanism since changes to the base class implementation might a ect all derived classes. e.g.,

class

Vector f

public

:

//:::

protected

:

// allow derived classes direct access T *buf ;

size t size ;

g;

class

Ada Vector :

public

Vector f

public

:

T &

operator return this

() (size t i)->buf [i];f

g// Note the strong dependency on the name buf

g;



However, performance and design reasons may dictate use of the protected access control speci er

{

Note, inline functions often reduces the need for these eciency hacks:::

(52)

Overview of Multiple Inheritance in C++



C++ allows multiple inheritance

{

i.e., a class can be simultaneously derived from two or more base classes

{

e.g.,

class

X f /* :::. */ g;

class

Y :

public

X f /* :::. */ g;

class

Z :

public

X f /* :::. */ g;

class

YZ :

public

Y,

public

Z f /* :::. */ g;

{

Derived classes Y, Z, and YZ inherit the data members and methods from their respective base classes

(53)

Multiple Inheritance Illustrated

Base Base

Derived Derived

11 DerivedDerived 12 12

Base Base

Derived Derived

22 NON

NON--VIRTUALVIRTUAL

INHERITANCE INHERITANCE

Base

Derived Derived

11 DerivedDerived 12 12

VIRTUAL VIRTUAL INHERITANCE INHERITANCE

vv vv

Derived Derived

22

(54)

Liabilities of Multiple Inheritance



A base class may legally appear only once in a derivation list, e.g.,

{ class

Two Vector :

public

Vector,

public

Vec- tor // ERROR!



However, a base class may appear multiple times within a derivation hierarchy

{

e.g., class YZ contains two instances of class

X



This leads to two problems with multiple inheritance:

1. It gives rise to a form of method and data member ambiguity

{

Explicitly quali ed names and additional meth- ods are used to resolve this

2. It also may cause unnecessary duplication of storage

{

\Virtual base classes" are used to resolve this

(55)

Motivation for Virtual Base Classes



Consider a user who wants an

Init Checked Vector

:

class

Checked Vector :

public virtual

Vector

f /* :::. */ g;

class

Init Vector :

public virtual

Vector

f /* :::. */ g;

class

Init Checked Vector :

public

Checked Vector,

public

Init Vector

f /* :::. */ g;



In this example, the

virtual

keyword, when

applied to a base class, causes

Init Checked Vector

to get one

Vector

base class instead of two

(56)

Overview of Virtual Base Classes



Virtual base classes allow class designers to specify that a base class will be shared among derived classes

{

No matter how often a virtual base class may occur in a derivation hierarchy, only \one" shared instance is generated when an object is instan- tiated

 Under the hood, pointers are used in derived classes that contain virtual base classes



Understanding and using virtual base classes correctly is a non-trivial task since you must plan in advance

{

Also, you must be aware when initializing sub- classes objects:::



However, virtual base classes are used to

implement the client and server side of

many implementations of CORBA distributed

objects

(57)

Virtual Base Classes Illustrated

Vector Vector

Checked Checked

Vector

Vector InitInit Checked Checked

Vector Vector

Vector Vector

Checked Checked

Vector Vector NON

NON--VIRTUALVIRTUAL

INHERITANCE INHERITANCE

Vector Vector

Checked Checked

Vector

Vector InitInit Checked Checked

Vector Vector VIRTUAL VIRTUAL INHERITANCE INHERITANCE

vv vv

Checked Checked

Vector Vector

(58)

Initializing Virtual Base Classes



With C++ you must chose one of two methods to make constructors work cor- rectly for virtual base classes:

1. You need to either supply a constructor in a virtual base class that takes no arguments (or has default arguments), e.g.,

Vector::Vector (size t size = 100); // has problems::: 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.,

Init Checked Vector (size t size,

const

T &init):

Vector (size), Check Vector (size), Init Vector (size, init)

(59)

Vector Interface Revised



The following example illustrates templates, multiple inheritance, and virtual base classes in C++

#

include

<iostream.h>

#

include

<assert.h>

// A simple-minded Vector base class, // no range checking, no initialization.

template

<

class

T>

class

Vector

f

public

:

Vector (size t s): size (s), buf (

new

T[s]) fg T &

operator

[] (size t i) f

return

this->buf [i]; g size t size (

void

)

const

f

return this

->size ; g

private

:

size t size ; T *buf ;

g;

(60)

Init Vector Interface



A simple extension to the Vector base class, that enables automagical vector initializa- tion

template

<

class

T>

class

Init Vector :

public virtual

Vector<T>

f

public

:

Init Vector (size t size,

const

T &init) : Vector<T> (size)

f

for

(size t i = 0; i < this->size (); i++) (*

this

)[i] = init;

g// Inherits subscripting operator and size().

g;

(61)

Checked Vector Interface



A simple extension to the Vector base class that provides range checked subscripting

template

<

class

T>

class

Checked Vector :

public virtual

Vector<T>

f

public

:

Checked Vector (size t size): Vector<T> (size) fg

T &

operator

[] (size t i)

throw

(RANGE ERROR) f

if

(

this

->in range (i))

return

(*(inherited *)

this

)[i];

else throw

RANGE ERROR (i);

g// Inherits inherited::size.

private

:

typedef

Vector<T> inherited;

bool

in range (size t i)

const

f

return

i <

this

->size ();

g

g;

(62)

Init Checked Vector Interface and Driver



A simple multiple inheritance example that provides for both an initialized and range checked Vector

template

<

class

T>

class public

Init Checked Vector :Checked Vector<T>,

public

Init Vector<T> f

public

Init Checked Vector (size t size,// Inherits Checked Vector::: VectorInit VectorChecked Vector<T><(size),T> (size, init),<T> (size)

operator

fg

const

[]T &init):

g;



Driver program

int

main (

try

fsize t sizesize t initInit Checked Vectorcout

int

argc,<< "vector size

char

==::atoi (argv::atoi (argv*argv<[]=

int

")[[>2f<<1]v (size, init);]););v.size ()

<< ", vector contents = ";

for

(size t icout <<=v[0; ii]; < v.size (); i++)

cout << "\n" << ++v[v.size () , 1] << "\n";

g

catch

(RANGE ERROR) f /* :::*/ g

g

(63)

Multiple Inheritance Ambiguity



Consider the following:

struct

Base 1 f

int

foo (

void

); /* :::. */ g;

struct

Base 2 f

int

foo (

void

); /* :::. */ g;

struct

Derived : Base 1, Base 2 f /* :::. */ g;

int

main (

void

) f Derived d;

d.foo (); // Error, ambiguous call to foo ()

g



There are two ways to x this problem:

1. Explicitly qualify the call, by pre xing it with the name of the intended base class using the scope resolution operator, e.g.,

d.Base 1::foo (); // or d.Base 2::foo ()

2. Add a new method foo to class Derived (similar to Ei el's renaming concept) e.g.,

struct

Derived : Base 1, Base 2 f

int

foo (

void

) f

Base 1::foo (); // either, both Base 2::foo (); // or neither

g

g;

(64)

Summary



Inheritance supports evolutionary, incre- mental development of reusable compo- nents by specializing and/or extending a general interface/implementation



Inheritance adds a new dimension to data abstraction, e.g.,

{

Classes (ADTs) support the expression of com- monality where the general aspects of an ap- plication are encapsulated in a few base classes

{

Inheritance supports the development of the application by extension and specialization with- out a ecting existing code:::



Without browser support, navigating through

complex inheritance hierarchies is dicult

: ::

References

Related documents

9.6 Overriding Base-Class Members in a Derived Class 9.7 Public, Protected and Private Inheritance.. 9.8 Direct Base Classes and Indirect

Schmidt Distributed Object Computing Group!. Motivation: the Distributed

FOREACH CONNECTION FOREACH CONNECTION INITIATE CONNECTION INITIATE CONNECTION ASYNC CONNECT ASYNC CONNECT INSERT IN REACTOR INSERT IN

: Message : Message Queue Queue : SOCK : SOCK Stream Stream : Input : Input Channel Channel : SOCK : SOCK Stream Stream : Reactor.

 Outline Blob Streaming Architecture and Related Patterns.. Evaluation

{ The communication protocol used between ORBs is often orthogonal to its connection establishment and service handler initialization protocol.

 Pseudo-code for server logging daemon active object void logging handler ( void ). f foreach (incoming logging records from client) call handle

 Private methods of the base class are not accessible to a derived class (unless the derived class is a friend of the base class).  If the subclass is derived