introduction to c++ programming module 3 inheritance and dynamic binding yaodong bi, ph.d....
TRANSCRIPT
Introduction to C++ ProgrammingModule 3
Inheritance and Dynamic Binding
Yaodong Bi, Ph.D.Department of Computing Sciences
University of Scranton(717)[email protected]
Outline
Module 1 - An Overview of C++ and OO Programming
Module 2 - Classes and Operator Overloading
Module 3 - Inheritance and Dynamic Binding
Module 4 - Function and Class Templates
Module 5 - C++ Stream I/O and Exception Handling
Review of Module 2Class data members and member functionsConstructors & destructorsCopy constructors and copy assignmentsConstant objects and constant member
functionsThe this pointerStatic members and member functionsClass objects as members of another classOperator overloadingFriend functions and classesConversion operators
Module 3: Inheritance and Dynamic Binding
Public Inheritance and Protected MembersConstructors and destructors in derived classesRedefine Base-class Members in Derived
ClassesType ConversionsPolymorphism and Virtual FunctionsVirtual Destructors Abstract Classes and Concrete classesMultiple InheritanceVirtual Base Classes
Example 1: Inheritance
CPersonm_name : stringm_age : int
GetName() costGetAge() constSetName(string)SetAge(int)Print() const
CManagerm_nMgrLevel : int
GetMgrLevel() constSetMgrLevel(int mgrLevel)Print() const
CEngineerm_nEngType : int
GetEngType() constSetEngType(int engType)Print() const
Example 1: Inheritance//person.h
#ifndef PERSON_H#define PERSON_H#include <string>using namespace std;
class CPerson {public:
CPerson(const string name, int age = 30);string GetName() const;int GetAge() const;void SetName(string);void SetAge(int);virtual void Print() const;virtual ~CPerson();
protected:string m_name;
private:int m_age;
};#endif
//manager.h
#ifndef MANAGER_H#define MANAGER_H#include "person.h"
class CManager: public CPerson{public:
CManager(string name, int age=25, int level=1);int GetMgrLevel() const;void SetMgrLevel(int level);void Print() const; // virtualvirtual ~CManager();
private:int m_nMgrLevel;
};#endif
Example 1: Inheritance - cont’d
//engineer.h
#ifndef ENGINEER_H#define ENGINEER_H
#include "person.h"
class CEngineer: public CPerson{public:
CEngineer(string name, int age=25, int engType = 1);
int GetEngType() const;void SetEngType(int engType);virtual void Print() const;virtual ~CEngineer();
private:int m_nEngType;
};#endif
//inheritance.cpp
#include <iostream>#include "manager.h"#include "engineer.h"
void main(){
CManager* mgrPtr1=new CManager("Bi"); CManager* mgrPtr2=
new CManager("Brown", 25, 20);CEngineer* engPtr=
new CEngineer("Smith", 20, 15);
mgrPtr1->SetName("Johnson");
CPerson* array[3]; array[0] = mgrPtr1;array[1] = engPtr; array[2] = mgrPtr2;for (int i=0; i<3; i++){ array[i]->Print(); cout << endl;
delete array[i];}
}
Example 1: Inheritance - cont’d//person.cpp
#include <iostream>#include "person.h"
using namespace std;
CPerson::CPerson(string name, int age)
:m_name(name), m_age(age){ }CPerson::~CPerson(){ }string CPerson::GetName() const{
return m_name;}int CPerson::GetAge() const{
return m_age;}
// person.cpp -- cont’d
void CPerson::SetName(string name){
m_name = name;}void CPerson::SetAge(int age){
m_age = age;}void CPerson::Print() const{
cout << "Name:\t\t" << m_name << endl;
cout << "Age:\t\t" << m_age << endl;
}
Example 1: Inheritance - cont’d//manager.cpp
#include <iostream>#include "manager.h"
CManager::CManager(string name, int age, int level)
:CPerson(name, age){
m_nMgrLevel = level;}CManager::~CManager(){}int CManager::GetMgrLevel() const{
return m_nMgrLevel;}
// manager.cpp - cont’d
void CManager::SetMgrLevel(int level)
{m_nMgrLevel = level;
}void CManager::Print() const{
CPerson::Print();cout << "Mngmnt Level:\t"
<< m_nMgrLevel << endl;
}
Example 1: Inheritance - cont’d//engineer.cpp
#include <iostream>#include "engineer.h"
using namespace std;
CEngineer::CEngineer(string name, int age, int engType)
:CPerson(name, age), m_nEngType(engType)
{}CEngineer::~CEngineer(){}
// engineer.cpp - cont’d
int CEngineer::GetEngType() const{
return m_nEngType;}void CEngineer::SetEngType(int
engType){
m_nEngType = engType;}void CEngineer::Print() const{
CPerson::Print();cout << "Eng Type:\t" << m_nEngType << endl;
}
Public Inheritance and Protected Members Base Classes or Superclasses
CPerson
Derived Classes or Subclasses CManager and CEngineer
Protected Members Protected members of a base class are accessible only by
the member functions and friends of the base class and member functions and friends of its derived classes.
Private members of a base class are only accessible by the member functions and friends of the base class.
EX: CPerson::m_name is accessible by CManager and CEngineer, and int CPerson::m_age is not accessible by either of them. void CManager::Print() const{ cout<<m_name; // okay
cout<<m_age; // illegal}
Public Inheritance and Protected Members Public Inheritance
Ex: class CManager: public CPerson { /*...*/}; The public and protected members of the base class are
inherited as public and protected members of the derived class.
Protected and private inheritanceEx: class CManager: protected CPerson { /*...*/};
// all public and protected members of CPerson become
// protected “members” of CManager
class CEngineer: private CPerson { /*...*/}; // all public and protected members of CPerson become
// private “members” of CEngineer A derived class via protected or private inheritance cannot be
assigned to the base class through either a pointer or reference.CEngineer *eng = new CEngineer(…);CPerson *prsn = eng; // illegal initialization.
Constructors and Destructors Initialize the base-class portion of a derived class
Call the base-class constructor in the member initialization.Ex: CManager:: CManager(string nm, int age, int lvl)
:CPerson(nm, age), m_nMgrLevel(lvl) { /*...*/};
A derived class constructor can specify initializers for its immediate bases only, not their individual members
Ex: CManager:: CManager(string nm, int age, int lvl):m_name(nm),m_age(age), m_nMgrLevel(lvl) { /*...*/};
Construction order of class objects first the base, the members , and the derived class itself. If a base is not initialized by an initializer, the default
constructor of the base would be called.
Destruction order of class objects first the derived class itself , the members, and the base. Reverse order of construction
Redefine Base-Class Member Functions A member function of the derived class masks all
the member functions of the base with the same name. Assume CPerson::Print() were not virtual
void CPerson::Print() const { /*...*/}void CManager::Print() const {/*...*/}
CManager mgr(..); mgr.Print(); // call CManager::Print() const;
void CManager::Print() const {/*...; Print(); // infinite recursive calls to
CManager::Print... */}
The scope resolution operator can be used to access the base class member function.
void CManager::Print() const {/*...; CPerson::Print(); // call CPerson::Print() const... */}
Type conversions CDerived* to CBase* and CDerived& to CBase&
CPerson *psnPtr;CManager mgr;CManager* mgrPtr = new CManager(...);psnPtr = mgrPtr; // no type cast neededCPerson& rPrsn = mgr; // no type cast neededmgrPtr = (CManager*) psnPtr; //explicit type case neededpsnPtr = new CPerson(...);mgrPtr = (CManager*) psnPtr; //dangerous
CDerived to CBase -- SlicingCManager mgr(...);CPerson prsn = mgr; //only the CPerson portion is copied
CBase to CDerived Not allowed -- leave the additional members undefined. Copy constructor and copy assignment may be defined
Polymorphism and Virtual Functions Non-polymorphic way -- Type fields and switch statements.
class CPerson { ...; public: int type;};
void Print(const CPerson* p){ switch (p->type) {
case PERSON:// specific to CPerson; break;
case MANAGER://specific to CManager; break;
... }
How about adding CSecretary? Virtual functions and dynamic binding
CPerson* array[4];array[0] = &mngr1;array[1] = &engineer; array[2] =
&mngr2;array[3]=&scrtry;for (int i=0; i<3; i++) array[i]->Print();
// the print() function of the actual object will be called
Polymorphism and Virtual Functions Operations vs methods
Operation: void CPerson::Print() const; // an operation Method: void CPerson::Print() const { /* …. */ } // a method
void CManager::Print() const { /* … */} // another method
To get polymorphic behavior, the member function must be virtual and objects are manipulated through pointers or references class CPerson { ... virtual void Print() const; ...}; // specify virtual CPersonr* ptrBase = new CManager(...);
ptrBase->Print(); // CManager::Print() is called CEngineer engineer(...); CPersonr& rBase = engineer;
rBase.Print(); //CEngineer::Print() is called CPersonr person = engineer; //slicing
ptrBase.Print(); // CPersonr::Print() is called Once a function is defined virtual, it remains virtual all the way
down the inheritance hierarchy. class CDirector: public CManager{..... void Print() const; ...};
Here, void CDirector::Print() const is virtual because void CPerson::Print() const is virtual
Polymorphism and Virtual Functions Redefined virtual functions must have the same return type and
signature as the base virtual function.class CEngineer: public CPerson { … void Print(int x); … }
// mask CPerson::Print because the extra paramter
class CDirector: public CManager { … void Print(); … } // mask CManager::print because it is not
constCDirector director(...); CPerson& ref = director;
ref.Pirnt(); // CManager::Print() will be called.
When a derived class does not redefine a virtual function, the derived class inherits its immediate base class’s virtual function.
class CDirector: public CManager {… } // no Print() is declared
CPerson& ref = director; ref.Pirnt(); // CManager::Print() will be called.
When the scope resolution operator :: is used for a function, the virtual mechanism will not be used.
void CDirector::Print() const {.. CManager::Print(); ...} //use a base class’s virtual function
CPerson* ptr= new CDirector(...);
ptr->CPerson::Print(); // call the CPerson::Print()
Virtual Destructors Since the base class cannot know whether a derived class
needs cleanup or not, a non-virtual destructor of the base class may cause inadequate cleanup.
class CBase {... ~CBase(); ...}; //regular destructor class CDerived: public CBase { ... ~CDerived(); char* ptr; ...}; // needs
cleanup
CBase* ptr = new CDerived(...);delete ptr; // CBase::~CBase() is called, not
CDerived::~CDerived()
To ensure proper cleanup, declare the base destructor virtualclass CBase {... virtual ~CBase(); ...}; // virtual destructor class CDerived: public CBase { ... ~CDerived(); char* ptr; ...};
CBase* ptr = new CDerived(...);delete ptr; // CDerived::~CDerived() will be called
Destruction order of class objects 1. the derived class itself ,
2. the members, and then
3. the base. Reverse order of construction
Abstract Classes and Concrete Classes Pure virtual functions
class CAbstract {... virtual void foo() =0; ...}; // foo is a pure virtual function
Abstract class: a class with one or more pure virtual functionsclass CAbstract is a virtual class
An abstract class cannot be instantiated. A concrete class is one that can be instantiated.
CAbstract abstract; // illegal
An abstract class is generally used to specify the pure interface of a type of objects.
class CAbsStack { virtual void Push(int)=0; // an abstract stack class virtual int Pop()=0; virtual ~CAbsStack(); };
class CStack: public CAbsStack // overrides all the pure virtuals { virtual void Push(int) {/*...*/} virtual int Pop() {/*...*/} virtual ~CStack();private: int* ptr, int top, int size }; // a concrete stack
classCStack stack; // a stack object
If a derived class does not override all the pure virtual functions inherited from its ancestors, it is an abstract class.
Multiple Inheritance// simple.cpp
#include <iostream>#include <string>using namespace std;
class CBase1{public:
CBase1(int x) {value=x;}int GetData() const {return value;}
protected:int value;
};class CBase2{public:
CBase2(char c) { letter=c;}char GetData() const {return letter;}
protected:char letter;
};
// simple.cpp -- cont’d
class CDerived: public CBase1, public CBase2
{public:
CDerived(int x, char c, string name):CBase1(x), CBase2(c), m_name(name) {}
string GetName() const { return m_name;}void Print() const;
private:string m_name;
};
void CDerived::Print() const{
cout<<"\nPrint in CDerived\n";cout<<m_name<<endl;cout<<value<<endl;cout<<letter<<endl;
}
Multiple inheritance Multiple inheritance: a derived
class inherits the members of more than one base classclass CDerived: public CBase1,
public CBase2 {}; The derived class can access
public and protected members of all the base classes.void CDerived::Print() const{... cout<<value<<endl;
cout<<letter<<endl; } When multiple base classes have
member functions with the same name, the scope resolution operator may be used.
CDerived d(20, 'B', "MetLife");cout<< d.CBase1::GetData() << d.CBase2::GetData();
// simple.cpp -- cont’d// main
void main(){
CDerived d(20, 'B', "MetLife");
cout<<"Print in Main() "<< endl << d.CBase1::GetData()<< endl << d.CBase2::GetData()<< endl << d.GetName()<< endl;
d.Print();}
Output:Print in Main Print in
CDerived20 MetLifeB 20MetLife B
Virtual Base ClassesA logical model for an employee example
CPerson
m_name : stringm_age : int
GetName() costGetAge() constSetName(string)SetAge(int)Print() const
CEngineer
m_nEngType : int
GetEngType()SetEngType(int)Print() const
CSalaried
m_nSalary : int
GetSalary()SetSalary(int)Print() constCSalariedEng
m_nDept : int
GetDept()SetDept(int)Print() const
Virtual Base Classes Logical model
Engineer is a Person, and so is Salaried. SalariedEng is a Person and a Salaried, so it inherits members of both Engineer and Salaried and Person. Logically, there is one Person for SalariedEng.
C++ implmentationclass CEngineer: public CPerson {...};class CSalaried: public CPerson {...};
Both CEngineer and CSalaried have their own copy of CPersonCSalariedEng* saleng = new CSalariedEng(...);
saleng->CEngineer::SetName(“newName”); would only change the CPerson portion of CEngineer. The CPerson portion of CSalaried would not be affected. CEngineer:: for SetName is required here.
Virtual base classes Declare CPerson virtual. Thus, only one copy of CPerson is kept
for CSalariedEng.class CEngineer: virtual public CPerson {...};class CSalaried: public virtual CPerson {...};
Virtual Base ClassesC++ implementation without using virtual base classes
CPerson
m_name : stringm_age : int
GetName() costGetAge() constSetName(string)SetAge(int)Print() const
CEngineer
m_nEngType : int
GetEngType()SetEngType(int)Print() const
CSalaried
m_nSalary : int
GetSalary()SetSalary(int)Print() const
CSalariedEng
m_nDept : int
GetDept()SetDept(int)Print() const
CPerson
m_name : stringm_age : int
GetName() costGetAge() constSetName(string)SetAge(int)Print() const
Virtual Base Classes Since there is only one copy of CPerson, the scope resolution
specifier is not needed for the members of CPerson. saleng->CEngineer::SetName(“newName”); // used to besaleng->SetName(“newName”); // okay
When both parents have the same virtual function, the derived class must define the virtual function. Otherwise, the compiler would not know which parent’s virtual function should be used for the derived class.
class CEngineer: virtual public CPerson {...void Print() const;...};
class CSalaried: virtual public CPerson {...void Print() const;...};since both CEngineer and CSalaried define Print(), CSalariedEng must define virtual void Print() const.
class CSalariedEng: public CEngineer, public CSalaried {... virtual void Print() const;...};
If a parent class does not declare its base class virtual, it has its own copy of the base class even when other parents have declared the same base virtual. The parents who have declared the same base virtual share a single copy of the base class.
Example 2: Virtual Base Classes -- cont’d//person.h
#ifndef PERSON_H#define PERSON_H#include <string>using namespace std;
class CPerson {public:
CPerson(const string name, int age = 30);
string GetName() const;int GetAge() const;void SetName(string);void SetAge(int);virtual void Print() const;virtual ~CPerson();
protected:string m_name;
private:int m_age;
};#endif
//engineer.h
#ifndef ENGINEER_H#define ENGINEER_H
#include "person.h"
class CEngineer: virtual public CPerson
{public:
CEngineer(string name, int age=25, int engType
= 1);
int GetEngType() const;void SetEngType(int engType);virtual void Print() const;virtual ~CEngineer();
private:int m_nEngType;
};
#endif
Example 2: Virtual Base Classes -- cont’d// salaried.h
#ifndef SALARIED_H#define SALARIED_H
#include <iostream>#include <string>#include "person.h"
using namespace std;
class CSalaried: public virtual CPerson
{public:
CSalaried(string nm, int age=30, int salary =30000);
int GetSalary() const;void SetSalary(int salary);virtual void Print() const;
private:int m_nSalary;
};#endif
//salariedempr.h
#include "salaried.h"#include "engineer.h"
class CSalariedEng: public CSalaried,
public CEngineer{public:
CSalariedEng(string nm, int age, int type, int salary, int dept);int GetDept() const;void SetDept(int dept);virtual void Print() const;
private:int m_nDept;
};
Example 2: Virtual Base Classes -- cont’d//inheritance.cpp
#include <iostream>#include "engineer.h"#include "salaried.h"#include "salariedengr.h"
int main(){
CSalariedEng sengr("BI", 30, 10, 30000, 304);
sengr.SetName("Someone");sengr.CPerson::Print();
CPerson& ref= sengr;ref.Print();
return 0;}
Example 2: Virtual Base Classes -- cont’d// salaried.cpp
#include "salaried.h"
CSalaried::CSalaried(string nm, int age, int salary)
:CPerson(nm, age), m_nSalary(salary){ }int CSalaried::GetSalary() const{
return m_nSalary;}void CSalaried::SetSalary(int salary){
m_nSalary = salary;}void CSalaried::Print() const{
cout<<"Salaried Emp:"<<endl;CPerson::Print();cout<<"Salary:\t\t$”
<<m_nSalary<<endl;}
// salariedengr.cpp#include "salariedengr.h"
CSalariedEng::CSalariedEng(string nm, int age, int type, int salary, int dept):CEngineer(nm, age, type), CSalaried(nm, age, salary) ,CPerson(nm, age)
{m_nDept = dept;
}int CSalariedEng::GetDept() const {
return m_nDept;}void CSalariedEng::SetDept(int dept) {
m_nDept = dept;}void CSalariedEng::Print() const{
cout<<"Salaried Engineer:"<<endl;CEngineer::Print();CSalaried::Print();cout<<"Department:\t"<<m_nDept<<endl;
}
Summary of Module 3
Public Inheritance and Protected MembersConstructors and destructors in derived classesRedefine Base-class Members in Derived
ClassesType ConversionsPolymorphism and Virtual FunctionsVirtual Destructors Abstract Classes and Concrete classesMultiple InheritanceVirtual Base Classes
Advice
Use pointers and references to avoid slicing and to ensure polymorphism
A derived class cannot access private members of its base class.
Use virtual functions to allow new implementations to be added without affecting user code
A class with a virtual function should have a virtual destructor even when the class does not need a destructor for itself.
Use abstract classes to keep implementation details out of interfaces
An abstract class typically does not need a constructor. Multiple inheritance should be used when a “is-a” relationship
exists between a new type and two or more existing types. Use virtual base classes to represent things that may be
shared by subclasses.
Programming Assignments Improve Example 2: Virtual Base Classes
Add a new class for hourly-paid employees. It has attributes for hourly-pay rate and the number of hours each week.
Add another class for hourly-paid engineers. In other words, it is a derived class from both engineer and hourly-paid.
Write a main() to test your new classes.
Design a class hierarchy for geometric objects Start with an abstract class Shape which specify the interface
-- operations like rotate, draw, etc common to all shapes. Design geometric objects from simple to complex: point,
circle, cylinder, etc. Simulate graphical features. Ex. Draw() may print the
geometric name of an object instead really drawing it on screen.
Design a logical model for all geometric shapes you can thinks of.
Implement your logical model in C++.