Stat 598w lecture 12
1 / 52

STAT 598W: Lecture 12 - PowerPoint PPT Presentation

  • Uploaded on

STAT 598W: Lecture 12. Purdue University More on Inheritance and Related Topics. Topics. Inheritance Polymorphism Virtual methods Abstract classes. Inheritance. Inheritance is the property that instances of a child class can access data and behavior of the parent class(s).

I am the owner, or an agent authorized to act on behalf of the owner, of the copyrighted work described.
Download Presentation

PowerPoint Slideshow about 'STAT 598W: Lecture 12' - cassia

An Image/Link below is provided (as is) to download presentation

Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author.While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server.

- - - - - - - - - - - - - - - - - - - - - - - - - - E N D - - - - - - - - - - - - - - - - - - - - - - - - - -
Presentation Transcript
Stat 598w lecture 12

STAT 598W: Lecture 12

Purdue University

More on Inheritance and Related Topics


  • Inheritance

  • Polymorphism

  • Virtual methods

  • Abstract classes


  • Inheritance is the property that instances of a child class can access data and behavior of the parent class(s).

    • Parent  superclass; child  subclass

    • A CEO is a manager is an employee

    • A dog is a mammal is an animal is a living thing...

Benefits of inheritance
Benefits of Inheritance

  • Software reusability: inherited behavior doesn't have to be rewritten, and thus will save time and likely be more reliable.

  • Code sharing: separate users use the same classes; two subclasses use facilities of a common superclass.

  • Consistent interface: by inheriting methods, the interface to subclasses will be largely consistent. Objects that are almost the same will have interfaces which are almost the same.

Benefits of inheritance1
Benefits of Inheritance

  • Software components: “off-the-shelf” software units, e.g., Microsoft's Microsoft Foundation Classes library, QuantLib, the Interactive Brokers C++ API.

  • Rapid prototyping: concentrate only on portions of a new system which are different than previous ones. Get something running sooner, for early evaluation.

Benefits of inheritance2
Benefits of Inheritance

  • Polymorphism:

    • Software is typically designed top-down, written bottom-up. Inheritance encourages abstract superclasses, specialized for particular circumstances.

    • So rather than having only very low-level code reusable, code at the highest level of abstraction can be reused.

    • Polymorphic objects can react differently depending on the type of input they are given.

Costs of inheritance
Costs of Inheritance

  • Execution speed: lots of function calls, very general methods for dealing with arbitrary subclasses. We are getting better at this…

  • Program size: a library of objects may have just what you want, but it may be “spread out.” Purpose-built code will likely be smaller.

  • Other types of complexity: flow of control in OOP programs can be just as hard to trace.

Implementation details
Implementation Details

  • Consider a class for employees:

class Employee {


string name;  

int age;  

int department;  

int salary;  

Employee * next; // link to employee list  

// ...


This example comes from Stroustrup

Managers are objects too
Managers Are Objects Too

  • So far, so good, but…

class Manager {  

Employee emp;   // manager's employee record  

Employee* group;    // people managed  

int level; // high, higher, highest 

// ...


A problem and a solution
A Problem, and a Solution

  • A manager is an employee, so Employee data is stored in the emp member of Manager. But how to get a Manager into the linked list of employees?

  • A Manager* is different from an Employee*.

  • A better way: a Manageris anEmployee, so make it a subclass:

class Manager : public Employee { // public inheritance  

Employee* group;  

short level;  

// ...


Subclass advantages
Subclass Advantages

  • Now we can create a list of employees, some of whom are managers:

Employee * makeList() {  

Manager m1, m2;  

Employee e1, e2;  

Employee * elist;  

elist = &m1;  // put m1 on elist = &e1;        // put e1 on elist = &m2;        // put m2 on elist = &e2;        // put e2 on elist = 0;  // terminate elist

return elist;


How does this work
How Does This Work?

  • This works because a manager is an employee, so an Employee * can point to a Manager.

  • This doesn't work the other way around, unless there is explicit pointer type conversion. (Which is really really dangerous!)

Let s add some methods
Let's Add Some Methods

class Employee {  

string name;  

// ...


Employee* next;  

void print();  

// ...


class Manager : public Employee {  

// ...


void print();  

// ...


This would seem natural
This Would Seem Natural

  • But derived classes can't access private portions of the base class. Why?

    • If they could, then private stuff wouldn't really be private.

    • Anyone could construct a derived class and have access.

void Manager::print() { 

cout << "name is " << name << '\n'; // error


The good solution
The Good Solution

  • Derived classes cannot access private members of the base class.

  • But they can access public members:

  • Note the use of the scope resolution operator, needed since print() is being redefined in Manager. (What happens if we forget to say Employee::?)

void Manager::print() {  

Employee::print();    // print employee info, then  

// print manager info


The not so good solution
The Not-So-Good Solution

  • Make name a protected member of the Employee class, so subclasses (like Manager) have access

  • This is generally a bad idea, but everyone does it.

  • Why bad? It breaks encapsulation; anyone can write a subclass to get access.

The inevitable student class
The Inevitable Student Class

#include <iostream>

#include <string>

using namespace std;

enum studentYear {freshman, sophomore, junior, senior, graduate};

class Student {


int studentID;

double gpa;

studentYear y;

string name;


Student(int id, double g, studentYear x, string nm);

void print() const ;


Gradstudent s are student s
GradStudents are Students

enum support { ta, ra, fellow, other};

class GradStudent: public Student {


support s;

string dept;

string thesis;


GradStudent(int id, double g, year x, string nm,

support t, string d, string th);

void print() const;


Student members have to

be protected for this to work

New concepts
New Concepts

  • Which base class members are accessible to derived classes? The choices are public, protected, private.

  • public means that protected and public members of Student are available to GradStudent. This is the normal (but not default) case.

  • Note that private members of the base class are never available to subclasses. Why?

  • It is typical for a subclass to add new members, both data and methods. Note print() is an overridden method, a concept different from overloading. The signature is the same, but the owner is different.

Code reuse with inheritance
Code Reuse With Inheritance

Student::Student(int id, double g, studentYear x, string nm)


gpa(g), y(x),

name(nm) {


GradStudent::GradStudent(int id, double g, studentYear x, string nm, support t, string d, string th)     

:Student(id, g, x, nm),

s(t), dept(d),

thesis(th) {


It’s common for the constructor of the derived class to call the base class constructor.Now e.g., name can be private.

The print methods
The print() Methods

void Student::print() const {

cout << name << ", " << studentID << ", "

<< (int)y << ", " << gpa;


void GradStudent::print() const {


cout << ", " << dept << ", " << (int)s

<< ", " << thesis;


Finally a driver program
Finally, a Driver Program

void main() {

Student s(365, 2.53, sophomore, "Larry Fine");

Student* ps = &s;

GradStudent gs(366, 2.03, graduate, "Curly Howard", ta,

"Theatre", "Nyuk Nyuk Nyuk");

GradStudent* pgs;


cout << endl;

ps = pgs = &gs; // implicit conversion of gradStudent* to student*

ps->print(); // student::print()

cout << endl;

pgs->print(); // gradStudent::print()

cout << endl;


Here is the output
Here is the Output

Larry Fine, 365, 1, 2.53

Curly Howard, 366, 4, 2.03

Curly Howard, 366, 4, 2.03, Theatre, 0, Nyuk Nyuk Nyuk

Static and dynamic typing
Static and Dynamic Typing

  • Binding time: the time when an attribute or meaning of a a program construct is determined.

  • For instance, in strongly typed languages, variable names are bound to variable types at compile time (e.g., int a). This leaves no room for variables to take on other types (c.f. variants in VBA).

  • Dynamically typed languages need some sort of run time system, for example to determine variable types and bind appropriate operators.

  • Essentially, the question is, are types associated with variables (names) or with values?

Static and dynamic typing1
Static and Dynamic Typing

  • If x and y are declared ints, then the compiler knows what to do with the expression x + y.

  • In OOP, there are additional problems. We saw before that pointers to base class types are valid pointers to subclass objects. This violates the spirit of “strong typing”.

  • Similarly, an object of a subclass type is a member of the superclass, so it is possible to make an assignment.

Employees and managers
Employees and Managers

void main() {

Employee e;

Manager m;






//m = e;              error: an e isn't an m

e = m;             // Legal: an m is an e

e.print(); // But, Employee::print() is called


The container problem
The “Container Problem”

  • If we view m as an Employee, can we ever recover that m is really a Manager?

  • We would like to write abstract “container classes” like sets and lists, but in C++, heterogeneous classes are harder than homogeneous ones.

  • But if we use pointers to elements of collections, things can be done.

Solving the problem
Solving the Problem

  • Given a pointer of type base *, how do we know if it points to an object of type base or to some derived type?

  • Three possible solutions:

    • Ensure that only objects of a single type are pointed to. This insists on homogeneous containers.

    • Place a “type field” in the base class for functions to inspect.

    • Use “virtual functions”.

Example of a type field
Example of a Type Field

struct employee { // note this is a struct; everything public

enum emp_type { M, E};

emp_type type;

employee * next;

char * name;

// ...


struct manager : employee { // also a struct

employee * group;

short level;

// ...


Then define
Then, Define

void employee_print(employee * e) {

  switch (e -> type) {

  case E:

    cout << e->name << '\t' << e->department << '\n';


  case M:

    cout << e->name << '\t' << e->department << '\n';

    manager * p = (manager*)e;

    cout << "level " << p->level << '\n';



This is a really bad idea. Don’t do it!

Printing the employee list
Printing the Employee List

  • A function to print employees might go like:

  • But what happens if a new subclass is defined? Go through all the code?

void f(employee * elist) {

for (; elist; elist = elist->next)



Static and dynamic binding
Static and Dynamic Binding

  • If a message is passed to a receiver, which method responds to the message?

  • On one hand, this is obvious: if the receiver knows its type, then it will perform the method associated with that type, or look upwards.

  • On the other hand, there has to be a mechanism to “find an object's type,” and this may be expensive at run time.

Static and dynamic binding1
Static and Dynamic Binding

  • Static binding if the declared object type determines the method to use.

  • Dynamic binding if the actual type (at run time) determines the method to use.

  • C++ “prefers” to use static binding, since it imposes no additional overhead.

  • C++ can be forced to use dynamic binding if necessary (the programmer has to ask for it explicitly).

Virtual functions
Virtual Functions

  • For dynamic binding, C++ gives us virtual functions.

  • The virtual keyword says that a function may be overridden in a subclass, and that the type of the object receiving a message should determine which method (function) to use.

Virtual function example
Virtual Function Example

class Base {


int i;

  virtual void print_i() {

cout << i << " inside Base\n";



class Derived : public Base {


virtual void print_i() {

        cout << i << " inside Derived\n";



This is new

Virtual function example1
Virtual Function Example

This yields:

1 inside Base

2 inside Derived

Even though pb was declared

a pointer to type Base, it may

point to a Derived object.

When it does, Derived's member

function is chosen.

void main() {

Base b;

Base* pb = &b;

Derived d;

b.i = 1

d.i = 2;


pb = &d;



Abstract classes
Abstract Classes

  • In the employee example, the base class “makes sense”, that is, we can conceive of objects of that type.

  • Sometimes this is not the case.

class Shape {


virtual void rotate(int) {



virtual void draw() {




Pure virtual functions
Pure Virtual Functions

  • Making a shape of this unspecified kind is rather pointless, since every operation on this object results in an error.

  • We can get the compiler to help us keep track by making Shape an abstract class.

  • This is done by defining one or more of its member functions as pure virtual.

Pure virtual functions1
Pure Virtual Functions

class Shape {

// ...


    virtual void rotate(int) = 0;

    virtual void draw() = 0;

    // ...


  • Now no objects of type Shape may be created.

  • An abstract class can only be used as a base class for derived types.

Let s use this stuff
Let’s Use This Stuff!

  • Some classes to represent arithmetic expressions:

    • Term (abstract)

    • Constant : public Term

    • BinaryOp : public Term (abstract)

    • Plus : public BinaryOp

Expression trees
Expression Trees


This is the “Composite”

design pattern















Starting the inheritance hierarchy
Starting the Inheritance Hierarchy

#include <iostream>

#include <sstream>

#include <string>

using namespace std;

class Term {


Term() {}

virtual ~Term() {}

virtual string symbolicEval() = 0;

virtual double numericalEval() = 0;


Our basic abstract class.

It has two pure virtual

methods, and a virtual


symbolicEval() writes

an expression like

((1.1 + 2.2) + 3.3)

numericalEval() evaluates

it: 6.6

The constant class
The Constant Class

This class is no longer

abstract, since the

pure virtuals are


symbolicEval() uses

an ostringstream

object that allows

“<<-ing” into a


class Constant : public Term {

double value;


Constant() { value = 0; }

Constant(double v) { value = v; }

virtual ~Constant() {}

virtual string symbolicEval() {

ostringstream oss;

oss << value;

return oss.str();


virtual double numericalEval() {

return value;



The binaryop class
The BinaryOp Class

class BinaryOp : public Term {


virtual ~BinaryOp() {

if (lChild) delete lChild;

if (rChild) delete rChild;



Term * lChild, * rChild;

BinaryOp(Term * l, Term * r) {

lChild = l;

rChild = r;



This is the parent class for

the binary arithmetic

operators. It centralizes

common construction

and destruction activities.

Note that this class is still


The Expression class in the

UML diagram is conceptually

nice, but not needed in


Plus a typical binary operator
Plus: A Typical Binary Operator

class Plus : public BinaryOp {


Plus(Term * l, Term * r) : BinaryOp(l, r) {}

virtual ~Plus() {}

virtual string symbolicEval() {

ostringstream oss;

oss << "(" << lChild->symbolicEval();

oss << " + ";

oss << rChild->symbolicEval() << ")";

return oss.str();


virtual double numericalEval() {

return (lChild->numericalEval() + rChild->numericalEval());



A simple driver
A Simple Driver

void main() {

Constant * c1 = new Constant(1.1);

Constant * c2 = new Constant(2.2);

Constant * c3 = new Constant(3.3);

Plus * p1 = new Plus(c1, c2);

Plus * p2 = new Plus(p1, c3);

cout << p2->symbolicEval() << " = ";

cout << p2->numericalEval() << endl;

delete p2;


Note that all Terms are created with new, and the tree is “held

together” with pointers. Pay particular attention to the way

delete works. Term’s destructor must be virtual for this to work!

Beware implicit conversions
Beware Implicit Conversions

class Base {


  virtual void foo(int) { cout << “Base::foo(int)” << endl;}

  virtual void foo(double) {cout << “Base::foo(double)” << endl;}



class Derived : public Base {


  virtual void foo(int) {cout << “Derived::foo(int)” << endl;}



Beware implicit conversions1
Beware Implicit Conversions

void main() {

Derived d;

Base b, *pb = &d;; // selects Base::foo(int); // selects Base::foo(double); // selects Derived::foo(int); // selects Derived::foo(int) overriden

pb->foo(9); // selects Derived::foo(int)

pb->foo(9.5); // selects Base::foo(double) virtual func


Pure virtual functions3
Pure Virtual Functions

  • A pure virtual function which is not defined in a derived class remains a pure virtual function, so that the derived class is also an abstract class.

  • This allows us to build implementations in stages.

Pure virtual functions4
Pure Virtual Functions

class X {


    virtual void f() = 0;

    virtual void g() = 0;


X b;  // error: declaration of object of abstract class X

class Y : public X {

    void f();  // overrides X::f


Y b;  // error: declaration of object of abstract class Y

class Z : public Y {

    void g();  // overrides X::g


Z c;  // this is OK