C++-inherit4 - Background The C++ Programming Language...

Info iconThis preview shows page 1. Sign up to view the full content.

View Full Document Right Arrow Icon
This is the end of the preview. Sign up to access the rest of the document.

Unformatted text preview: Background The C++ Programming Language Object-oriented programming is often dened as the combination of Abstract Data Types ADTs with Inheritance and Dynamic Binding Single and Multiple Inheritance in C++ Each concept addresses a di erent aspect of system decomposition: Douglas C. Schmidt www.cs.wustl.edu schmidt schmidt@cs.wustl.edu Washington University, St. Louis 2. Inheritance decomposes systems into three-dimensional hierarchies of modules Inheritance relationships form a lattice" 1. ADTs decompose systems into two-dimensional grids of modules Each module has public and private interfaces 3. Dynamic binding enhances inheritance e.g., defer implementation decisions until late in the design phase or even until run-time! 2 1 Data Abstraction vs. Inheritance Motivation for Inheritance Inheritance allows you to write code to handle certain cases and allows other developers to write code that handles more specialized cases, while your code continues to work DATA ABSTRACTION DATA ABSTRACTION (2 DIMENTIONAL) (2 DIMENTIONAL) Inheritance partitions a system architecture 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 INHERITANCE (3 DIMENTIONAL) 3 4 Inheritance Overview Visualizing Inheritance A type called a subclass or derived type can inherit the characteristics of another types called a superclass or base type Base 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 Derived 1 Change implementations without changing the base class interface  Most useful when combined with dynamic binding Derived 3 Derived 2 Derived 5 Derived 4 2. Generalizations Extensions Derived 6 Add new operations or data to derived classes 5 Types of Inheritance Inheritance comes in two forms, depending on number of parents a subclass has 1. Single Inheritance SI Only one parent per derived class 6 Inheritance Trees vs. Inheritance DAGs Derived 1 Base Form an inheritance tree" SI requires a small amount of run-time overhead 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 runtime overhead also involving dynamic binding e.g., C++, Ei el, Flavors a LISP dialect 7 Derived 3 Base 1 Derived 3 INHERITANCE TREE Derived 1 INHERITANCE DAG Derived 2 Derived 4 Base 2 Derived 4 8 Inheritance Bene ts Inheritance Liabilities 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 without changing existing code Using well-tested base classes helps reduce bugs in applications that use them 1. May create deep and or wide hierarchies that are di cult to understand and navigate without class browser tools 2. May decrease performance slightly i.e., when combined with multiple inheritance and dynamic binding Reduce object code size 2. Enhance extensibility and comprehensibility Helps support more exible and extensible architectures along with dynamic binding i.e., supports the open closed principle Often useful for modeling and classifying hierarchicallyrelated domains 3. Without dynamic binding, inheritance has only limited utility Likewise, dynamic binding is almost totally useless without inheritance 4. Brittle hierarchies, which may impose dependencies upon ancestor names 10 9 Key Properties of C++ Inheritance Inheritance in C++ Deriving a class involves an extension to the C++ class declaration syntax The class head is modi ed to allow a derivation 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; 11 The base derived class relationship is explicitly 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, polymorphic 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 operations at some point in their inheritance hierarchy 12 Simple Screen Class Subclassing from Screen 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 ; class Screen 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; can be a public base class of ::: g; 14 13 Multiple Levels of Derivation The Screen Inheritance Hierarchy 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 ; Point Screen Color ::: g; Window ::: Menu class Menu inherits data and methods from both Window and Screen i.e., sizeof Menu = sizeof Window = sizeof Screen 15 Screen Window Menu hierarchy 16 Variations on a Screen Using the Screen Hierarchy ::: e.g., ps1 : Screen 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 &; ps2 : Screen g; w: Window stand-alone function Some processing omitted s- dump o; *s- vptr 1  s, o; void dump image Screen *s, ostream &o f Menu g Screen s; Window w; Menu m; Bit Vector bv; A pointer to a derived class can be assigned 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; 17 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; 18 Specialization Example 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 functionality supplied by the derived class This is directly supported in C++ via abstract base classes and pure virtual functions 19 Inheritance may be used to obtain the features of one data type in another closely related data type For example, class Date represents an arbitrary Date: class Date f public: int m, int d, int y; Date virtual void print ostream &s const; private: month , day , year ; int g; ::: Class Birthday derives from Date, adding a name eld representing the person's birthday, e.g., class Birthday : public Date f public: Birthday const char *n, int m, int d, int y : Date m, d, y, person strdup n fg ~Birthday void f free person ; g virtual void print ostream &s const; private: char *person ; const ::: g; 20 Implementation and Use-case Alternatives to Specialization Birthday::print could print the person's name as well as the date, e.g., Note that we could also use object composition instead of inheritance for this example, e.g., void Birthday::print ostream &s const f s this- person " was born on "; class Birthday f public Birthday char *n, int m, int d, int y: g Date::print s; s " n"; date m, d, y, person n fg same as before private: Date date ; char *person ; e.g., 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; 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 reusability, it does a ect extensibility ::: 21 22 Extension Generalization Example Using Inheritance for Extension Generalization Derived classes add state variables and or operations to the properties and operations 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 derived classes 23 Using class Vector as a private base class for derived class Stack class Stack : private Vector f * ::: * g; In this case, Vector's operator may be reused as an implementation for the Stack push and pop methods Note that using private inheritance ensures that does not show up in the interface for class Stack! operator Often, a better approach in this case is to use a composition Has-A rather than a descendant Is-A relationship ::: 24 Vector Interface Using class Vector as a base class for a derived class such as class Checked Vector or class Ada 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 initialization 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; 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! private: g; Vector Implementation g T *buf ; size t size ; destructor automatically called 25 26 Bene ts of Inheritance Inheritance enables modi cation and or extension of ADTs without changing the original source code e.g., someone may want a variation on the basic 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, initialized vectors, etc. This is done by de ning new derived classes that inherit the characteristics of the Vector base class Note that inheritance also allows code to be shared 27 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; 28 Implementation of Checked Vector Checked Vector Use-case e.g., template class T bool Checked Vector T ::in range size t i const f return i this- size ; e.g., include "Checked Vector.h" g typedef Checked Vector int CV INT; template class T int foo int size f try 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; f CV INT cv size; int i = cv cv.size  ; Error detected! exception raised Call base class destructor ::: g catch RANGE ERROR g f* ::: *g g 29 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 g inherited::foo ; g; This scheme obviously doesn't work as transparently for multiple inheritance 30 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; ::: 31 32 Ada Vector Use-case 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 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; 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; 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; g g 33 g Vector destructor called when ada vec goes out of scope catch RANGE ERROR f * ::: *g 34 Memory Layout Memory layouts in derived classes are created by concatenating memory from the base classes 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; g; Base Class Constructor Constructors are called from the bottom up" Destructors are called from the top down" e.g., * Vector constructor * Ada Vector The derived class constructor calls the base constructor in the base initialization section," i.e., Ada Vector T ::Ada Vector size t lo, size t hi : inherited hi , lo + 1, lo bnd lo fg 35 struct Vector * ct 6VectorFi struct Vector * 0this, size t 0s f if  0this jj  0this = nw FUi sizeof struct Vector g  0this- size 6Vector = 0s,  0this- buf 6Vector = nw FUi sizeof int * 0s; return 0this; 36 Derived Class Constructors Destructor 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 Note, destructors, constructors, and assignment operators are not inherited However, they may be called automatically 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 38 37 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 relationship 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 notion of inheritance Has-A vs. Is-A Relationships CONSUMER RELATIONSHIP Vector DESCENDANT RELATIONSHIP Stack Vector Checked Vector Ada Vector Descendants are used to describe an Is-A relationship 39 40 Interface vs. Implementation Inheritance Class inheritance can be used in two primary ways: 1. Interface inheritance: a method of creating a subtype of an existing class for purposes of setting up dynamic binding, e.g., Circle is a subclass of Shape i.e., Is-A relation 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 implementation 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 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 harmful 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 minimizes 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 ::: 42 41 Private vs Public vs Protected Derivation Access control speci ers i.e., public, private, protected are also meaningful in the context of inheritance In the following examples: ::: ::: Public Derivation e.g., class A f public: public A protected: protected A private: g; . represents actual omitted code private A class B : public A f public: . is implicit Note, all the examples work for both data members and methods 43 public A public B protected: protected A protected B private: private B g; 44 Protected Derivation Private Derivation e.g., e.g., class A f public: public A private: private A protected: class A f public: public A protected: protected A private: g; g; protected A class B : private A f public: public B protected: protected B private: class B : protected A f public: public B protected: also class B : A protected A public A protected B private: private B g; public A protected A private B g; private A 46 45 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 M E M B E R A C C E S S INHERITANCE ACCESS +-----------+-----+-----+-----+ | public | pub | pro | pri | +-----------+-----+-----+-----+ | protected | pro | pro | pri | +-----------+-----+-----+-----+ | private | n a | n a | n a | +-----------+-----+-----+-----+ p p p u r r b o i l t v Other Uses of Access Control Speci ers Selectively rede ne visibility of individual methods from base classes that are derived privately class A f public: int f ; int g ; private: int p ; ::: g; Note that the resulting access is always the most restrictive of the two 47 class B : private A f public: A::f; Make public protected: g; A::g ; Make protected 48 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: Private methods of the base class are not accessible to a derived class unless the derived class is a friend of the base class class B : private A f nor protected nor public! public: g; A::p ; General Rules for Access Control Speci ers ERROR! It is also an error to derive publically and then try to selectively decrease the visibility of base class methods in the derived class e.g., you may not say: 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 accessible to derived classes and friends only class B : public A f private: g; A::f; ERROR! 50 49 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: Overview of Multiple Inheritance in C++ C++ allows multiple inheritance ::: allow derived classes direct access T *buf ; size t size ; g; class Ada Vector : public Vector f public: T &operator size t i f return this- buf i ; 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 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 ::: Note, inline functions often reduces the need for these e ciency hacks ::: 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 INHERITANCE Derived 1 Derived 12 class Two Vector : public Vector, public Vec- Base tor However, a base class may appear multiple times within a derivation hierarchy Derived 2 e.g., class X VIRTUAL INHERITANCE Derived 1 Derived 12 YZ contains two instances of class This leads to two problems with multiple inheritance: Base v ERROR! 1. It gives rise to a form of method and data member ambiguity Explicitly quali ed names and additional methods are used to resolve this 2. It also may cause unnecessary duplication of storage Virtual base classes" are used to resolve this v Derived 2 54 53 Overview of Virtual Base Classes 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 55 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 instantiated  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 subclasses objects ::: However, virtual base classes are used to implement the client and server side of many implementations of CORBA distributed objects 56 Virtual Base Classes Illustrated Vector Checked Vector NON-VIRTUAL INHERITANCE Init Checked Vector Initializing Virtual Base Classes Vector With C++ you must chose one of two methods to make constructors work correctly for virtual base classes: Checked Vector 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., Vector v Checked Vector VIRTUAL INHERITANCE Init Checked Vector v Init Checked Vector size t size, const T &init: Vector size, Check Vector size, Init Vector size, init Checked Vector 57 58 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; 59 Init Vector Interface A simple extension to the Vector base class, that enables automagical vector initialization template class T class Init Vector : public virtual Vector T f public: Init Vector size t size, const T &init f g g; : Vector T size for size t i = 0; i this- size ; i++ *this i = init; Inherits subscripting operator and size. 60 ::: Init Checked Vector Interface and Driver 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; A simple multiple inheritance example that provides for both an initialized and range checked Vector template Checked Vector : lass T classpublic cChecked Vector T , public Init Vector T f Init public: Checked Vector size t size, const T &init: Init g; Driver program int main fint argc, char *argv  f try size t size = ::atoi argv 1 ; size t init = ::atoi argv 2 ; Init Checked Vector int v size, init; cout "vector size = " v.size  ", vector contents = "; for size t i =v0; ;i v.size ; i++ cout i bool in range size t i const f return i this- size ; g; g 61 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 ::: ::: ::: g Derived d; d.foo ; Error, ambiguous call to foo  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 g; g Base 1::foo ; Base 2::foo ; Vector T size, Init Vector T size, init, Checked Vector T size fg Inherits Checked Vector::operator either, both or neither 63 g cout " catch RANGEn" ++v v.size  , 1 ERROR f* *g " n"; ::: 62 Summary Inheritance supports evolutionary, incremental development of reusable components 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 commonality where the general aspects of an application are encapsulated in a few base classes Inheritance supports the development of the application by extension and specialization without a ecting existing code ::: Without browser support, navigating through complex inheritance hierarchies is di cult ::: 64 ...
View Full Document

Ask a homework question - tutors are online