1.09k likes | 1.45k Views
Topics on Inheritance. Subtyping and Code Reuse Typing Conversions and Visibility Abstract Base Classes Multiple Inheritance Inheritance and Design Detailed C++ Considerations ##. Traits Passed Through Inheritance. But Mommy, where did my blue eyes come from?. 2.
E N D
Topics on Inheritance • Subtyping and Code Reuse • Typing Conversions and Visibility • Abstract Base Classes • Multiple Inheritance • Inheritance and Design • Detailed C++ Considerations ##
Traits Passed Through Inheritance But Mommy, wheredid my blue eyescome from? 2
The Inheritance Mechanism • Means of deriving new class from existing classes, called base classes • Reuses existing code eliminating tedious, error prone task of developing new code • Derived class developed from base by adding or altering code • Hierarchy of related types created that share code & interface ##
Single and Multiple Inheritance Single inheritance occurs when single base class Multiple inheritance occurs when more thanone base class
Credit Cards - Single Inheritance Same basic features Each is a little different
Voice Mail - Multiple Inheritance Voice mail has featuresof both mail and phone
Taxonomic Classification (1 of 3) Elephant and mouse both mammals • Descriptions succinct Root concept "mammal" • Warm-blooded • Higher vertebrates • Nourish young using milk-producing mammary glands
Taxonomic Classification (2 of 3) Mammal Warm blooded Higher vertebrate Nourish young with milk-producing glands Mouse Elephant
Taxonomic Classification (3 of 3) • In C++ terms, classes elephant and mouse derived from base class "mammal" • In OOP terms, elephant ISA mammal describes relationship • If circus had elephants, then object circus might have members of type elephant • Class circus HASA elephant describes subpart relationship ##
Virtual Member Functions • Functions declared in base class and redefined in derived class • Class hierarchy defined by public inheritance creates related set of user types, all of whose objects may be pointed at by a base class pointer • By accessing virtual function through this pointer, C++ selects appropriate function definition at run-time ##
Pure Polymorphism • Object being pointed at must carry around type information so distinction can be made dynamically • Feature typical of OOP code • Each object "knows" how it is acted on • Inheritance designed into software to maximize reuse and allow natural modeling of problem domain ##
The OOP Design Methodology 1. Decide on an appropriate set of types 2. Design in their relatedness 3. Use inheritance to share code among classes
A Derived Class • Class derived from an existing class • class classname:(public|protected|private)optbasename{ member declarations}; • Keyword class replaced by struct with members public by default • Keywords public, protected, and private used to specify how base class members are accessible to derived class ##
A Base Class: student • class student { • public: • enum year { fresh, soph, junior, senior, • grad }; • student(char* nm, int id, double g, • year x); • void print() const; • protected: • int student_id; • double gpa; • year y; • char name[30]; • };
A Derived Class: grad_student • class grad_student : public student { • public: • enum support { ta, ra, fellowship, other }; • grad_student(char* nm, int id, double g, • year x, support t, char* d, char* th); • void print() const; • protected: • support s; • char dept[10]; • char thesis[80]; • };
Inheriting from the Base Class • Derived class is modification of base class that inherits public and protected members of base class • In grad_student, student members are inherited • student_id gpa • name year • print##
Add New Members in Derived Class • Derived class adds new members to existing class members • grad_student has three new data members and redefined member function • s • dept • thesis • print()##
Benefits of Inheritance • Code is reused • grad_student uses tested code from student • Reflects relationship in problem domain • Special grouping grad student outgrowth of real world and treatment of this group • Polymorphic mechanisms allow client code to treat inherited class as subtype of base class • Simplifies code, maintains subtype distinctions##
Public Inheritance (is a subtype) • Publicly inherited remain public • Private members can't be inherited • Protected members remain protected ## student shape grad_student ellipse polygon
Private Inheritance (is not a subtype) • Public members become private • Protected members become private • Code reuse mechanism ## generic tree private string tree
Typing Conversions and Visibility • Publicly derived class is subtype of base • Variable of derived class treated as if it were base class type • Pointer type pointer-to-base-class can point to objects of derived class type • Subtle implicit conversions occur between base and derived type • Difficult to follow what member is accessed if base and derived class overloaded same member name##
Students and Graduate Students In many respects, they are the same, but in some they differ.
More on the student Program (1 of 7) • student::student(char* nm, int id, double g, year x) • :student_id(id), gpa(g), y(x){ strcpy(name, nm);} • Constructor for base class does series of simple initializations • Calls strcpy() to copy student's name ##
More on the student Program (2 of 7) • //publicly derived • grad_student::grad_student(char* nm, int id, double g, year x, support t, • char* d, char* th) • :student(nm, id, g, x), s(t) • { strcpy(dept, d); • strcpy(thesis, th); } • Constructor for student invoked as part of initializer list • Logically base class object needs to be constructed first before object can be completed • student_id and gpa are protected which makes them visible only to the derived class
More on the student Program (3 of 7) • Reference to derived class may be implicitly converted to a reference to public base class • grad_student gs("Morris Pohl", 200, 3.2564, • grad, ta, "Pharmacy", "Retail Pharmacies"); • student& rs = gs; //alias • student* ps = &gs; //pointer init • Variable rs is reference to student • Base class of grad_student is student Reference conversion is appropriate
More on the student Program (4 of 7) • void student::print() • { • cout << name << " , " << student_id • << " , " << y << " , " << gpa << endl; • } • void grad_student::print() • { • student::print(); //base class info • cout << dept << " , " << s << endl << thesis << endl; • } • Infinite loop if not scope-resolved student::print()
More on the student Program (5 of 7) • #include "student.h" • main() //Test pointer conversion rules • { • student s("Mae Pohl", 100, 3.425, fresh), • *ps = &s; • grad_student gs("Morris Pohl",200, 3.2564, • grad, ta, "Pharmacy", • "Retail Pharmacies"), *pgs; • ps —> print(); //student::print • ps = pgs = &gs; • ps —> print(); //student::print • pgs —> print(); //grad_student::print • }
More on the student Program (6 of 7) • main() declares both class variables and pointers to them • Conversion rule - pointer to publicly derived class may be converted implicitly to pointer to its base class • Pointer ps can point to objects of both classes, but pointer pgs can point only at objects of type grad_student##
More on the student Program (7 of 7) • First ps -> print() invokes student::print • ps = pgs = &gs; both pointers pointing at object of type grad_student and assignment to ps involves implicit conversion • Second ps -> print(); invokes student::print • Irrelevant that pointer points at grad_student variable gs## • pgs -> print(); invokes grad_student::print • pgs is of type pointer to grad_student and, when invoked with an object of this type, selects a member function from this class
Creating a New vect Class “We can rewrite this safe-array code to include a new vect_bnd class with dynamic bounds checking. We’ll use vect as a base class, and let vect_bnd use inheritance so we don’t have to repeat all the vect code. It’s basic functions serve our purposes exactly.”
Dynamic Array Bounds (1 of 2) • Subscripting & assignment use these properties • Right side, lvalue automatically dereferenced • Left side specifies where value is stored • Safe array vect_bnd produced by deriving it from vect and invoking appropriate constructors • function-header : base-class-name (args)##
Dynamic Array Bounds (2 of 2) • Safe array has constructors, destructor and overloaded subscripting operator • Constructors convert ordinary integer array to safe array by allocating sufficient memory • Upper and lower bounds checked • Subscript operator [] overloaded with function which tests for out-of-bounds condition on array access##
Using Dynamic Array Bounds • Reuse code and extend vect type to safe array with dynamic bounds • More flexible and allows indices to correspond directly to problem domain • Example: Fahrenheit temperatures of water in its liquid state are is 32—212 degrees • Lower bound of 32 and upper bound of 212 • Safe array vect checked array bounds for in range and created arrays using free store##
Dynamic Array Bounds (1 of 8) • class vect { • public: //constructors & destructor • vect(); //create a size 10 array • vect(int l); //create a size l array • vect(const vect& v); //init by vect • vect(int a[], int l); //init by array • ~vect() { delete [] p; } • int ub() const {return (size—1);} • int& operator[](int i); //range checked • vect& operator=(vect& v); • vect operator+(vect& v); • private: • int *p; //base pointer • int size; //number of elements • };
Dynamic Array Bounds (2 of 8) • class vect_bnd: public vect { • public: • vect_bnd(); • vect_bnd(int, int); • int& operator[](int); • int ub() const { return (u_bnd); } //accessor • int lb() const { return (l_bnd); } • private: • int l_bnd, u_bnd; • }; • Derived type members l_bnd and u_bnd privately store lower and upper bounds
Dynamic Array Bounds (3 of 8) • Derived type reuses base type's representation and code • Derived class constructors invoke base class constructors • Syntax is same as member initialization • function header: base—class—name(args)##
Dynamic Array Bounds (4 of 8) • vect_bnd::vect_bnd() :vect(10) • { l_bnd = 0; • u_bnd = 9; • } • vect_bnd::vect_bnd(int lb, int ub) : • vect(ub — lb + 1) • { l_bnd = lb; • u_bnd = ub; • } • Additional code initializes bound's pair • Derived constructors call base constructors l_bnd u_bnd 0 1 2 3 4 5 6 7 8 9
Dynamic Array Bounds (5 of 8) • Alternatively, could be done in initializing list • vect_bnd::vect_bnd(int lb, int ub) • vect(ub — lb + 1), l_bnd(lb), • u_bnd(ub) {}
Dynamic Array Bounds (6 of 8) • int& vect_bnd::operator[](int i) • { • if (i < l_bnd || u_bnd < i) { • cerr << "index out of range" << endl; • exit(1); • } • return (vect::operator[](i — l_bnd)); • } • Reuse code in overloading indexing operator [] • Very inefficient - checking bounds twice
Dynamic Array Bounds (7 of 8) • To avoid inefficient double bounds check, make two changes • First change access privilege of vect::p to protected so derived class has direct access to previously private implementation of vect • Allows us to make second change of using p in vect_bnd::operator[]()##
Dynamic Array Bounds (8 of 8) • int& vect_bnd::operator[](int i) • { • if (i < l_bnd || u_bnd < i) { • cerr << "index out of range\n"; • exit(1); • }; • return (p[i — l_bnd]); • }
Determining Access Privilege • Tradeoff in code reuse and efficiency • Inheritance requires thinking about three access boundaries • What is to be strictly private and what is to be protected depends on what is reusable ##
Dynamic Virtual Function Selection • Typically base has virtual function and derived have their versions of function • Pointer to base class can point at either base or derived class objects • Member function selected depends on class of object being pointed at, not on pointer type • In absence of derived type member, base class virtual function used by default ##
Virtual & Overloaded Function Selection • Overloaded member function is compile-time selected based on signature • It can have distinct return types • Once declared virtual, this property is carried along to all redefinitions in derived classes • virtual modifier not needed in derived functions##
Virtual Function Selection (1 of 2) • #include <iostream.h> • class B { • public: • int i; • virtual void print_i() const { cout << i << " inside B" << endl; } • }; • class D: public B { //virtual too • public: • void print_i() const • { cout << i << " inside D" << endl; } • };
1 inside B 2 inside D Virtual Function Selection (2 of 2) • int main() • { • B b; • B* pb = &b; //point at a B object • D f; • f.i = 1 + (b.i = 1); • pb —> print_i(); //call B::print_i() • pb = &f; //point at a D object • pb —> print_i(); //call D::print_i() • }
Comments on the virt Program • Different print_i() executed • Dynamically selected on object pointed at • "Object sent message print_i and selects its corresponding version of method" • Pointer's base type is not determining method (function) selection • Different class objects processed by different functions at run-time • ADTs, inheritance, and process of objects dynamically are essentials of OOP ##
Confusion with Overloading (1 of 2) • Member function overloading and virtual functions cause mix-ups and confusion • class B { • public: • virtual foo(int); • virtual foo(double); • . . . • }; • class D: public B { • public: • foo(int); • . . . • };
Confusion with Overloading (2 of 2) • main() • { • D d; • B b, *pb = &d; • b.foo(9.5); //selects B::foo(double); • d.foo(9.5); //selects D::foo(int); • pb —> foo(9.5); //B::foo(double); • } • Base class function B::foo(int) overriden in derived class • Base class function B::foo(double) hidden in derived class