Inheritancenotes
Inheritancenotes
Inheritance is the capability of one class to acquire properties and characteristics from another class. The
class whose properties are inherited by other class is called the Parent or Base or Super class. And, the
class which inherits properties of other class is called Child or Derived or Sub class.
Inheritance makes the code reusable. When we inherit an existing class, all its methods and fields
become available in the new class, hence code is reused.
NOTE : All members of a class except Private, are inherited
Purpose of Inheritance
1. Code Reusability
2. Method Overriding (Hence, Runtime Polymorphism.)
3. Use of Virtual Keyword
1
Table showing all the Visibility Modes
Types of Inheritance
In C++, we have 5 different types of Inheritance. Namely,
1. Single Inheritance
2. Multiple Inheritance
3. Hierarchical Inheritance
4. Multilevel Inheritance
5. Hybrid Inheritance (also known as Virtual Inheritance)
2
Single Inheritance
In this type of inheritance one derived class inherits from only one base class. It is the simplest form of
Inheritance.
Multiple Inheritance
In this type of inheritance a single derived class may inherit from two or more than two base classes.
3
Hierarchical Inheritance
In this type of inheritance, multiple derived classes inherits from a single base class.
Multilevel Inheritance
In this type of inheritance the derived class inherits from a class, which in turn inherits from some other
class. The Super class for one, is sub class for the other.
4
Hybrid (Virtual) Inheritance
Hybrid Inheritance is combination of Hierarchical and Multilevel Inheritance.
Single Inheritance
5
Distance(ft, in) //call base constructor
{ sign = sg; } //set the sign
#include <iostream>
using namespace std;
////////////////////////////////////////////////////////////////
class A //base class
{
private:
int privdataA; //(functions have the same access
protected: //rules as the data shown here)
int protdataA;
public:
int pubdataA;
};
////////////////////////////////////////////////////////////////
class B : public A //publicly-derived class
{
public:
void funct()
{
int a;
a = privdataA; //error: not accessible
a = protdataA; //OK
a = pubdataA; //OK
}
6
};
////////////////////////////////////////////////////////////////
class C : private A //privately-derived class
{
public:
void funct()
{
int a;
a = privdataA; //error: not accessible
a = protdataA; //OK
a = pubdataA; //OK
}
};
////////////////////////////////////////////////////////////////
int main()
{
int a;
B objB;
a = objB.privdataA; //error: not accessible
a = objB.protdataA; //error: not accessible
a = objB.pubdataA; //OK (A public to B)
C objC;
a = objC.privdataA; //error: not accessible
a = objC.protdataA; //error: not accessible
a = objC.pubdataA; //error: not accessible (A private to C)
return 0;
}
Levels of inheritance
// multiple levels of inheritance
#include <iostream>
using namespace std;
const int LEN = 80; //maximum length of names
////////////////////////////////////////////////////////////////
class employee
{
private:
char name[LEN]; //employee name
unsigned long number; //employee number
public:
void getdata()
{
cout << "\n Enter last name: "; cin >> name;
cout << " Enter number: "; cin >> number;
}
void putdata() const
{
cout << "\n Name: " << name;
cout << "\n Number: " << number;
}
};
////////////////////////////////////////////////////////////////
class manager : public employee //manager class
{
private:
char title[LEN]; //"vice-president" etc.
double dues; //golf club dues
public:
void getdata()
{
employee::getdata();
cout << " Enter title: "; cin >> title;
cout << " Enter golf club dues: "; cin >> dues;
}
void putdata() const
{
employee::putdata();
cout << "\n Title: " << title;
cout << "\n Golf club dues: " << dues;
}
};
////////////////////////////////////////////////////////////////
class scientist : public employee //scientist class
{
private:
int pubs; //number of publications
public:
void getdata()
{
employee::getdata();
cout << " Enter number of pubs: "; cin >> pubs;
9
}
void putdata() const
{
employee::putdata();
cout << "\n Number of publications: " << pubs;
}
};
////////////////////////////////////////////////////////////////
class laborer : public employee //laborer class
{
};
////////////////////////////////////////////////////////////////
class foreman : public laborer //foreman class
{
private:
float quotas; //percent of quotas met successfully
public:
void getdata()
{
laborer::getdata();
cout << " Enter quotas: "; cin >> quotas;
}
void putdata() const
{
laborer::putdata();
cout << "\n Quotas: " << quotas;
}
};
////////////////////////////////////////////////////////////////
int main()
{
laborer l1;
foreman f1;
10
string grade;
public: //no-arg constructor
Type() : dimensions("N/A"), grade("N/A")
{ }
//2-arg constructor
Type(string di, string gr) : dimensions(di), grade(gr)
{ }
void gettype() //get type from user
{
cout << " Enter nominal dimensions (2x4 etc.): ";
cin >> dimensions;
cout << " Enter grade (rough, const, etc.): ";
cin >> grade;
}
void showtype() const //display type
{
cout << "\n Dimensions: " << dimensions;
cout << "\n Grade: " << grade;
}
};
////////////////////////////////////////////////////////////////
class Distance //English Distance class
{
private:
int feet;
float inches;
public: //no-arg constructor
Distance() : feet(0), inches(0.0)
{ } //constructor (two args)
Distance(int ft, float in) : feet(ft), inches(in)
{ }
void getdist() //get length from user
{
cout << " Enter feet: "; cin >> feet;
cout << " Enter inches: "; cin >> inches;
}
void showdist() const //display distance
{ cout << feet << "\'-" << inches << '\"'; }
};
////////////////////////////////////////////////////////////////
class Lumber : public Type, public Distance
{
private:
int quantity; //number of pieces
double price; //price of each piece
public: //constructor (no args)
Lumber() : Type(), Distance(), quantity(0), price(0.0)
{ }
//constructor (6 args)
Lumber( string di, string gr, //args for Type
int ft, float in, //args for Distance
int qu, float prc ) : //args for our data
Type(di, gr), //call Type ctor
Distance(ft, in), //call Distance ctor
quantity(qu), price(prc) //initialize our data
{ }
void getlumber()
{
Type::gettype();
Distance::getdist();
cout << " Enter quantity: "; cin >> quantity;
cout << " Enter price per piece: "; cin >> price;
11
}
void showlumber() const
{
Type::showtype();
cout << "\n Length: ";
Distance::showdist();
cout << "\n Price for " << quantity
<< " pieces: $" << price * quantity;
}
};
////////////////////////////////////////////////////////////////
int main()
{
Lumber siding; //constructor (no args)
//constructor (6 args)
Lumber studs( "2x4", "const", 8, 0.0, 200, 4.45F );
int main()
{
Lumber siding; //constructor (no args)
//constructor (6 args)
Lumber studs( "2x4", "const", 8, 0.0, 200, 4.45F );
Virtual functions
Normal functions access with pointers (early binding)
// notvirt.cpp
// normal functions accessed from pointer
#include <iostream>
using namespace std;
////////////////////////////////////////////////////////////////
class Base //base class
{
public:
void show() //normal function
{ cout << "Base\n"; }
16
};
////////////////////////////////////////////////////////////////
class Derv1 : public Base //derived class 1
{
public:
void show()
{ cout << "Derv1\n"; }
};
////////////////////////////////////////////////////////////////
class Derv2 : public Base //derived class 2
{
public:
void show()
{ cout << "Derv2\n"; }
};
////////////////////////////////////////////////////////////////
int main()
{
Derv1 dv1; //object of derived class 1
Derv2 dv2; //object of derived class 2
Base* ptr; //pointer to base class
Output
Base
Base
Output
Derv1
Derv2
18
Virtual functions and Polymorphism
// virtpers.cpp
// virtual functions with person class
#include <iostream>
using namespace std;
////////////////////////////////////////////////////////////////
class person //person class
{
protected:
char name[40];
public:
void getName()
{ cout << " Enter name: "; cin >> name; }
void putName()
{ cout << "Name is: " << name << endl; }
virtual void getData() = 0; //pure virtual func
virtual bool isOutstanding() = 0; //pure virtual func
};
////////////////////////////////////////////////////////////////
class student : public person //student class
{
private:
float gpa; //grade point average
public:
void getData() //get student data from user
{
person::getName();
cout << " Enter student's GPA: "; cin >> gpa;
}
bool isOutstanding()
{ return (gpa > 3.5) ? true : false; }
};
////////////////////////////////////////////////////////////////
class professor : public person //professor class
{
private:
int numPubs; //number of papers published
public:
void getData() //get professor data from user
{
person::getName();
cout << " Enter number of professor's publications: ";
cin >> numPubs;
}
bool isOutstanding()
{ return (numPubs > 100) ? true : false; }
};
////////////////////////////////////////////////////////////////
int main()
{
person* persPtr[100]; //array of pointers to persons
int n = 0; //number of persons on list
char choice;
do {
cout << "Enter student or professor (s/p): ";
19
cin >> choice;
if(choice=='s') //put new student
persPtr[n] = new student; // in array
else //put new professor
persPtr[n] = new professor; // in array
persPtr[n++]->getData(); //get data for person
cout << " Enter another (y/n)? "; //do another person?
cin >> choice;
} while( choice=='y' ); //cycle until not 'y'
int main()
{
D obj;
obj.show();
}
In this case both class B and C inherits function show() from class A. Hence class D has two inherited
copies of function show(). In main() function when we call function show(), then ambiguity arises, because
compiler doesn't know which show() function to call. Hence we use Virtual keyword while inheriting class.
20
// normbase.cpp
// ambiguous reference to base class
class Parent
{
protected:
int basedata;
};
class Child1 : public Parent
{ };
class Child2 : public Parent
{ };
class Grandchild : public Child1, public Child2
{
public:
int getdata()
{ return basedata; } // ERROR: ambiguous
};
// virtbase.cpp
// virtual base classes
class Parent
{
protected:
int basedata;
};
class Child1 : virtual public Parent // shares copy of Parent
{ };
class Child2 : virtual public Parent // shares copy of Parent
{ };
class Grandchild : public Child1, public Child2
{
public:
int getdata()
{ return basedata; } // OK: only one copy of Parent
};
Upcasting in C++
Upcasting is using the Super class's reference or pointer to refer to a Sub class's object. Or we can say
that, the act of converting a Sub class's reference or pointer into its Super class's reference or pointer is
called upcasting.
21
The opposite of upcasting is downcasting, in which we convert Super class's reference or pointer into
derived class's reference or pointer.
Upcasting and downcasting are an important part of C++. Upcasting and downcasting gives a possibility to build
complicated programs with a simple syntax. It can be achieved by using Polymorphism.
C++ allows that a derived class pointer (or reference) to be treated as base class pointer. This is upcasting.
Downcasting is an opposite process, which consists in converting base class pointer (or reference) to derived class
pointer.
Upcasting and downcasting should not be understood as a simple casting of different data types
As you can see, Manager and Clerk are both Employee. They are both Person too. What does it mean? It means
that Manager and Clerk classes inherit properties of Employee class, which inherits properties of Person class.
For example, we don't need to specify that both Manager and Clerk are identified by First and Last name, have
salary; you can show information about them and add a bonus to their salaries. We have to specify these properties
only once in the Employee class:
In the same time, Manager and Clerk classes are different. Manager takes a commission fee for every contract, and
Clerk has information about his Manager:
22
#include <iostream>
using namespace std;
class Person
{
//content of Person
};
int main()
{
//pointer to base class object
Employee* emp;
//implicit upcasting
emp = &m1;
//It's ok
cout<<emp->FirstName<<endl;
cout<<emp->salary<<endl;
Output
Steve
3000
Happy Birthday!!!
First Name: Kevin Last Name: Jones Salary: 1200
Happy Birthday!!!
First Name: Steve Last Name: Kent Salary: 3200
Manager of Kevin is Steve
24
Both upcasting and downcasting do not change object by itself. When you use upcasting or downcasting you just
"label" an object in different ways.
Upcasting is a process of treating a pointer or a reference of derived class object as a base class pointer. You do
not need to upcast manually. You just need to assign derived class pointer (or reference) to base class pointer:
When you use upcasting, the object is not changing. Nevertheless, when you upcast an object, you will be able to
access only member functions and data members that are defined in the base class:
int main()
{
//pointer to base class object
Employee* emp;
//implicit upcasting
emp = &m1;
//It's ok
cout<<emp->FirstName<<endl;
cout<<emp->salary<<endl;
congratulate(&c1);
congratulate(&m1);
One of the biggest advantage of upcasting is the capability of writing generic functions for all the classes that are
derived from the same base class. Look on example:
25
void congratulate(Employee* emp)
{
cout << "Happy Birthday!!!" << endl;
emp->show();
emp->addBonus(200);
};
This function will work with all the classes that are derived from the Employee class. When you call it with objects of
type Manager and Person, they will be automatically upcasted to Employee class:
//automatic upcasting
congratulate(&c1);
congratulate(&m1);
Memory layout
As you know, derived class extends properties of the base class. It means that derived class has properties (data
members and member functions) of the base class and defines new data members and member functions.
Of course, this model is simplified view of memory layout for objects. However, it represents the fact that when you
use base class pointer to point up an object of the derived class, you can access only elements that are defined in
the base class (green area). Elements of the derived class (yellow area) are not accessible when you use base
class pointer.
Downcasting
Downcasting is an opposite process for upcasting. It converts base class pointer to derived class pointer.
Downcasting must be done manually. It means that you have to specify explicit type cast.
Downcasting is not safe as upcasting. You know that a derived class object can be always treated as base class
object. However, the opposite is not right. For example, a Manager is always a Person; But a Person is not always a
Manager. It could be a Clerk too.
26
//pointer to base class object
Employee* emp;
//object of derived class
Manager m1("Steve", "Kent", 3000, 0.2);
//implicit upcasting
emp = &m1;
//explicit downcasting from Employee to Manager
Manager* m2 = (Manager*)(emp);
This code compiles and runs without any problem, because emp points to an object of Manager class.
What will happen, if we try to downcast a base class pointer that is pointing to an object of base class and not to an
object of derived class?
e1 object is not an object of Manager class. It does not contain any information about commission. That why such
an operation can produce unexpected results.
When you try to downcast base class pointer (Employee) that is not actually pointing up an object of derived class
(Manager), you will get access to the memory that does not have any information about derived class object (yellow
area). This is the main danger of downcasting.
You can use a safe cast that can help you to know, if one type can be converted correctly to another type. For this
purpose, use dynamic cast.
27
Dynamic Cast
dynamic_cast is an operator that converts safely one type to another type. In the case, the conversation is possible
and safe, it returns the address of the object that is converted. Otherwise, it returns nullptr.
dynamic_cast<new_type> (object)
If you want to use dynamic cast for downcasting, base class should be polymorphic - it must have at least one
virtual function. Modify base class Person by adding a virtual function:
Now you can use downcasting for converting Employee class pointers to derived classes pointers.
In this case, dynamic cast returns nullptr. Therefore, you will see a warning message.
RTTI can only be used with polymorphic types. This means that with each class you make, you must have at least
Compatibility note: On some compilers you have to enable support of RTTI to keep track of dynamic types.
So to make use of dynamic_cast (see next section) you have to enable this feature. See you compiler documentation
Dynamic_cast
The dynamic_cast can only be used with pointers and references to objects. It makes sure that the result of the
type conversion is valid and complete object of the requested class. This is way a dynamic_cast will always be
successful if we use it to cast a class to one of its base classes. Take a look at the example:
28
class Base_Class { };
class Derived_Class: public Base_Class { };
The first dynamic_cast statement will work because we cast from derived to base. The second dynamic_cast
statement will produce a compilation error because base to derived conversion is not allowed with dynamic_cast
If a class is polymorphic then dynamic_cast will perform a special check during execution. This check ensures that
// dynamic_cast
#include <iostream>
#include <exception>
using namespace std;
int main () {
try {
Base_Class * ptr_a = new Derived_Class;
Base_Class * ptr_b = new Base_Class;
Derived_Class * ptr_c;
In the example we perform two dynamic_casts from pointer objects of type Base_Class* (namely ptr_a and ptr_b)
29
If everything goes well then the first one should be successful and the second one will fail. The pointers ptr_a and
ptr_b are both of the type Base_Class. The pointer ptr_a points to an object of the type Derived_Class. The pointer
ptr_b points to an object of the type Base_Class. So when the dynamic type cast is performed then ptr_a is pointing
to a full object of class Derived_Class, but the pointer ptr_b points to an object of class Base_Class. This object is
Because this dynamic_cast fails a null pointer is returned to indicate a failure. When a reference type is converted
with dynamic_cast and the conversion fails then there will be an exception thrown out instead of the null pointer.
With dynamic_cast it is also possible to cast null pointers even between the pointers of unrelated classes.
pointer or reference, because the polymorphic mechanism takes care of it. In some cases the programmer wants
to know if an object of a derived class is used. Then the programmer can make use of dynamic_cast. (If the dynamic
cast is successful, then the pointer will point to an object of a derived class or to a class that is derived from that
derived class.) But there are circumstances that the programmer (not often) wants to know the prizes data-type.
Variables
Expressions
Data-types
#include <iostream>
#include <typeinfo>
using namespace std;
int main ()
{
int * a;
int b;
a=0; b=0;
if (typeid(a) != typeid(b))
{
cout << "a and b are of different types:\n";
cout << "a is: " << typeid(a).name() << '\n';
30
cout << "b is: " << typeid(b).name() << '\n';
}
return 0;
}
The result of a typeid is a const type_info&. The class type_info is part of the standard C++ library and contains
information about data-types. (This information can be different. It all depends on how it is implemented.)
A bad_typeid exception is thrown by typeid, if the type that is evaluated by typeid is a pointer that is preceded by
Virtual Destructors
Destructors in the Base class can be Virtual. Whenever upcasting is done, Destructors of the Base class
must be made virtual for proper destruction of the object when the program exits.
NOTE : Constructors are never Virtual, only Destructors can be Virtual.
int main()
{
Base* b = new Derived; //upcasting
delete b;
}
In the above example, delete b will only call the Base class destructor, which is undesirable because,
then the object of Derived class still remains because its destructor is never called. Which results in
memory leak.
Upcasting with Virtual Destructor
31
Now let’s see. what happens when we have Virtual destructor in the base class.
class Base
{
public:
virtual ~Base() {cout << "Base Destructor\t"; }
};
int main()
{
Base* b = new Derived; //upcasting
delete b;
}
Output :
Derived Destructor
Base Destructor
When we have Virtual destructor inside the base class, then first Derived class's destructor is called and
then Base class's destructor is called.
32