Inheritance - 2
1
Content
• Constructor and Destructor in Inheritance
• Inheritance and Static Functions
• Virtual class
• Abstract Class
• Pure Virtual Functions
Constructors and Destructors in
Base and Derived Classes
• Derived classes can have their own constructors and destructors.
• When an object of a derived class is created, the base class’s
constructor is executed first, followed by the derived class’s
constructor.
• When an object of a derived class is destroyed, its destructor is called
first, then that of the base class.
3
//EXAMPLE ~derived()
#include<iostream>
Using namespace std; { cout << "Destructing
//base class derived\n"; }
class base { };
public:
base() int main()
{ cout << "Constructing base\n"; } {
~base()
{ cout << "Destructing base\n"; }
derived ob;
}; // do nothing but
//derived class construct and destruct
Program Output
class derived: public base { ob Constructing base
public: Constructing derived
derived() returnDestructing
0; derived
Destructing base
{ }
cout << "Constructing derived\n"; }
4
Constructors and Destructors with Multiple Base
Classes
• Constructors are called in order of derivation, left to right, as
specified in derived's inheritance list.
• Destructors are called in reverse order, right to left.
5
//MULTI-LEVEL class derived2: public
#include <iostream> derived1 {
using namespace std; public:
class base {
derived2() { cout <<
public: "Constructing derived2\n";
base() }
{ cout << "Constructing base\n"; } ~derived2() { cout <<
~base() { cout << "Destructing "Destructing derived2\n"; }
base\n"; }
};
};
class derived1 : public base { int main()
public: { Program Output:
derived1() { cout << "Constructing Constructing base
derived2 ob;
Constructing derived1
derived1\n"; }
// construct andderived2
Constructing destruct ob
~derived1() { cout << "Destructing Destructing derived2
derived1\n"; } return Destructing
0; derived1
}; } Destructing base
6
//MULTIPLE BASE CASES class derived: public base1,
#include <iostream> public base2 {
using namespace std; public:
class base1 { derived() { cout <<
public: "Constructing derived\n"; }
base1() { cout << "Constructing
base1\n"; } ~derived() { cout <<
"Destructing derived\n"; }
~base1() { cout << "Destructing
base1\n"; } };
}; int main()
class base2 { {
public:
derived ob;
Program Output:
base2() { cout << "Constructing Constructing base1
base2\n"; } // construct and destruct
Constructing base2 ob
~base2() { cout << "Destructing return 0;Constructing derived
base2\n"; } Destructing derived
} Destructing base2
}; Destructing base1
7
Passing Arguments to Base Class
Constructor
derived-constructor(arg-list) : base1(arg-list),
base2(arg-list),
// ...
baseN(arg-list)
{
// body of derived constructor
}
8
Order of Constructor Call
• Base class constructors are always called in the derived class
constructors.
• Whenever you create derived class object, first the base class default
constructor is executed and then the derived class's constructor
finishes execution.
9
//EXAMPLE // derived uses x; y is passed along
#include <iostream> to base.
using namespace std; derived(int x, int y): base(y)
class base { { j=x;
protected: cout << "Constructing derived\n"; }
~derived() { cout << "Destructing
int i;
derived\n"; }
public:
void show() { cout << i << " " << j
base(int x) << "\n"; }
{ i=x; cout << "Constructing base\ };
n"; } int main()
~base() { cout << "Destructing {
base\n"; }
derived ob(3, 4);
};
ob.show(); // displays 4 3
class derived: public base {
return 0;
int j;
}
public:
10
//MULTIPLE BASE CASES class derived: public base1, public
#include <iostream> base2 {
int j;
using namespace std;
class base1 { public:
protected: int i; derived(int x, int y, int z): base1(y),
base2(z)
public:
{ j=x; cout << "Constructing derived\
base1(int x) { i=x; cout << n"; }
"Constructing base1\n"; }
~derived() { cout << "Destructing
~base1() { cout << "Destructing derived\n"; }
base1\n"; }};
void show() { cout << i << " " << j <<
class base2 { " " << k << "\n"; }
protected: int k; };
public: int main()
base2(int x) { k=x; cout << { derived ob(3, 4, 5);
"Constructing base2\n"; }
ob.show(); // displays 4 3 5
~base2() { cout << "Destructing
return 0; }
base1\n"; } };
11
Here’s what actually happens when base is instantiated:
• Memory for base is set aside
• The appropriate Base constructor is called
• The initialization list initializes variables
• The body of the constructor executes
• Control is returned to the caller
12
Here’s what actually happens when derived is
instantiated:
• Memory for derived is set aside (enough for both the Base and
Derived portions)
• The appropriate Derived constructor is called
• The Base object is constructed first using the appropriate Base
constructor. If no base constructor is specified, the default
constructor will be used.
• The initialization list initializes variables
• The body of the constructor executes
• Control is returned to the caller
13
Points to note
• It is important to understand that arguments to a base-class
constructor are passed via arguments to the derived class' constructor.
• Even if a derived class‘ constructor does not use any arguments, it will
still need to declare one if the base class requires it.
• In this situation, the arguments passed to the derived class are simply
passed along to the base.
14
Why is Base class Constructor called inside Derived class?
• Constructors have a special job of initializing the object properly.
• A Derived class constructor has access only to its own class members,
but a Derived class object also have inherited property of Base class,
and only base class constructor can properly initialize base class
members.
• Hence all the constructors are called, else object wouldn't be
constructed properly.
15
// This program contains an int main() {
error and will not compile. derived3 ob;
#include <iostream> ob.i = 10; // this is
using namespace std; ambiguous, which i???
class base { ob.j = 20;
public: int i; }; ob.k = 30;
class derived1 : public base { // i ambiguous here, too
public: int j; }; ob.sum = ob.i + ob.j + ob.k;
// also ambiguous, which i?
class derived2 : public base {
cout << ob.i << " ";
public: int k; };
cout << ob.j << " " << ob.k ;
class derived3 : public
derived1, public derived2 { cout << ob.sum;
return 0;
public: int sum; };
}
16
Discussion
• which ‘i’ is being referred to, the one in derived1 or the one in
derived2?
• Because there are two copies of base present in object ob, there are
two ob.is! ->, the statement is inherently ambiguous.
• There are two ways to remedy the preceding program.
• The first is to apply the scope resolution operator to i and manually
select one i. Example (on next slide).
17
// This program uses explicit int main()
scope resolution to select i. {
#include <iostream> derived3 ob;
using namespace std; ob.derived1::i = 10; // scope
class base { public: int i; }; resolved, use derived1's i
// derived1 inherits base. ob.j = 20;
class derived1 : public base { ob.k = 30;
public: int j; }; // scope resolved
// derived2 inherits base. ob.sum = ob.derived1::i + ob.j +
ob.k;
class derived2 : public base {
// also resolved here
public: int k; };
cout << ob.derived1::i << " ";
class derived3 : public cout << ob.j << " " << ob.k << " ";
derived1, public derived2 {
cout << ob.sum;
public: int sum; };
return 0; }
18
Discussion
• As you can see, because the :: was applied, the program has manually
selected derived1's version of base.
• What if only one copy of base is actually required? Is there some way
to prevent two copies from being included in derived3?
• This solution is achieved using virtual base classes.
20
• When two or more objects are derived from a common base class, you
can prevent multiple copies of the base class from being present in an
object derived from those objects by declaring the base class as virtual
when it is inherited.
• You accomplish this by preceding the base class' name with the
keyword virtual when it is inherited.
• For example, here is another version of the example program in which
derived3 contains only one copy of base:
Example Program Next
Slide
21
// This program uses virtual base classes. int main()
#include <iostream>
{
using namespace std;
class base { public: int i; }; derived3 ob;
ob.i = 10; // now unambiguous
// derived1 inherits base as virtual.
ob.j = 20;
class derived1 : virtual public base { ob.k = 30;
public: int j; }; // unambiguous
ob.sum = ob.i + ob.j + ob.k;
// derived2 inherits base as virtual.
// unambiguous
class derived2 : virtual public base { cout << ob.i << " ";
public: int k; }; cout << ob.j << " " << ob.k << " ";
cout << ob.sum;
class derived3 : public derived1, public
derived2 return 0;
{ public: int sum; }; }
22
Discussion
• As you can see, the keyword virtual precedes the rest of the inherited
class‘specification.
• Now that both derived1 and derived2 have inherited base as virtual,
any multiple inheritance involving them will cause only one copy of
base to be present.
• Therefore, in derived3, there is only one copy of base and ob.i = 10 is
perfectly valid and unambiguous.
23
Inheritance and Static Functions
• They are inherited into the derived class.
• If you redefine a static member function in derived class, all the other
overloaded functions in base class are hidden.
• Static Member functions can never be virtual.
24
Virtual base class
• They are used to prevent the confusion or duplicity among child
classes during inheritance.
• Consider the following situation:
class A {
public: void show() {cout<<"In A"<<endl;}
};
class B : public A {};
class C : public A {};
class D : public B, public C {};
int main() {D d; d.show(); }
Output
[Error] request for member 'show' is ambiguous
Need virtual keyword to fix this error
class A {
public: void show() {cout<<"In A"<<endl;}
};
class B : public virtual A {};
class C : public virtual A {};
class D : public B, public C {};
int main() {D d; d.show(); }
Abstract base class - interface
• It describes the capabilities of a C++ class without committing to a
particular implementation
• A class is made abstract by declaring at least one of its functions
as pure virtual function i.e. by placing "= 0" in its declaration
class Animal{
public:
virtual void sound() = 0;
void sleeping() {cout<<"Sleeping"; }
};
class Dog: public Animal{
public:
void sound() {cout<<"Woof"<<endl;}
};
int main(){
Dog obj; obj.sound(); obj.sleeping();
}
Output:
Woof
Sleeping
class Animal{
public:
virtual void sound() = 0;
void sleeping() {cout<<"Sleeping"; }
};
class Dog: public Animal{
public:
void sound() {cout<<"Woof"<<endl;}
};
int main(){
Animal *obj = new Dog;
obj->sound();
}
Output:
Woof
Abstract Class
• An abstract class is designed to act as base class. It is a design concept
in program development and provides a base upon which other
classes may be built.
• Abstract Class is a class which contains atleast one Pure Virtual
function in it. Abstract classes are used to provide an Interface for its
sub classes. Classes inheriting an Abstract Class must provide
definition to the pure virtual function, otherwise they will also
become abstract class.
34
Characteristics of Abstract Class
• Abstract class cannot be instantiated, but pointers and references of
Abstract class type can be created.
• Abstract class can have normal functions and variables along with a
pure virtual function.
• Classes inheriting an Abstract Class must implement all pure virtual
functions, or else they will become Abstract too.
35
Pure Virtual Functions
• Pure virtual Functions are virtual functions with no definition.
• They start with virtual keyword and ends with = 0.
• Syntax for a pure virtual function,
virtual void fun() = 0;
36
//Abstract base class int main()
class Base { {
public: //Base obj; (Compile Time
virtual void show() = 0; }; Error)
class Derived:public Base { Base *b;
public: Derived d;
void show() b = &d;
{ b->show();
cout << "Implementation of return 0;
Virtual Function in }
Derived class";
}
};
37
Note:
• Pure Virtual functions can be given a small definition in the Abstract
class, which you want all the derived classes to have. Still you cannot
create object of Abstract class.
• Also, the Pure Virtual function must be defined outside the class
definition. If you will define it inside the class definition, complier will
give an error. Inline pure virtual definition is Illegal.
38
Polymorphism
Conten
ts
• Polymorphism
• Function Overloading
• Default Function Arguments
• Ambiguity in Function Overloading
Polymorphi
sm
• The word polymorphism means having many
forms. In simple words, we can define
polymorphism as the ability of a message to
be displayed in more than one form.
• Real life example of polymorphism, a person at
a same time can have different characteristic.
Like a man at a same time is a father, a
husband, a employee.
• So a same person posses have different
behavior in different situations. This is called
polymorphism.
Types of
Polymorphism
• Compile Time Polymorphism
• Run Time Polymorphism
Compile Time
Polymorphism
• Early binding refers to events that occur at compile time. In essence, early
binding occurs when all information needed to call a function is known at
compile time. (Put differently, early binding means that an object and a function
call are bound during compilation).
• Examples of early binding include normal function calls (including
standard library functions), overloaded function calls, and overloaded
operators.
• The main advantage to early binding is efficiency. Because all information
necessary to call a function is determined at compile time, these types of
function calls are very fast.
Run Time
Polymorphism
• The opposite of early binding is late binding.
• As it relates to C++, late binding refers to function calls that are not
resolved until run time.
• As function calls are not determined at compile time, the object and the
function are not linked until run time.
Run Time
Polymorphism
• The main advantage to late binding is flexibility.
• Unlike early binding, late binding allows you to create programs that
can respond to events occurring while the program executes without
having to create a large amount of "contingency code."
• Keep in mind that because a function call is not resolved until run time,
late binding can make for somewhat slower execution times.
• Virtual functions are used to achieve late binding.
Function
Overloading
• Function overloading is the process of using the same name for
two or more functions.
• The secret to overloading is that each redefinition of the function
must use either different types of parameters or a different number
of parameters.
• It is only through these differences that the compiler knows which
function to call in any given situation.
Example
#include <iostream>
using namespace std;
int myfunc(int i); // these differ in types of parameters
double myfunc(double i);
int main()
{
cout << myfunc(10) << " "; // calls myfunc(int i)
cout << myfunc(5.4); // calls myfunc(double i)
return 0;
}
double
myfunc(double i)
{
return i;
}
Output:
int myfunc(int i) 10 5.4
{
return i;
}
Exampl
e2
#include <iostream>
using namespace std;
int myfunc(int i); // these differ in number of parameters
int myfunc(int i, int j);
int main()
{
cout << myfunc(10) << " "; // calls myfunc(int i)
cout << myfunc(4, 5); // calls myfunc(int i, int
j)
return 0;
}
int
myfunc(int i)
{
return i;
}
Output:
int myfunc(int i, int j) 10 20
{
return i*j;
}
Key
Points
• The key point about function overloading is that the functions must differ in regard to the types
and/or number of parameters. Two functions differing only in their return types cannot be
overloaded.
• For example, this is an invalid attempt to overload myfunc( ):
int myfunc(int i);
float myfunc(int i);
// Error: differing return types are insufficient when overloading.
• Sometimes, two function declarations will appear to differ, when in
fact they do not. For example, consider the following declarations.
void f(int *p);
void f(int p[]); // error, *p is same as p[]
• Remember, to the compiler *p is the same as p[ ]. Therefore, although the two prototypes appear
to differ in the types of their parameter, in actuality they do not.
Exampl
e3
Example 4
(Function overloading in
classes)
Inheritance based
Polymorphism
Example
Extend the
Logic
Virtual Functions
• Be aware that the virtual function mechanism works only with
pointers to objects and, with references, not with objects themselves.
Class Activity (15
minutes)
• Can you showcase polymorphism (Drive function in various types
of automobiles) in context of VEHICLES ?
Overloading vs overriding
• Function overloading – same function name but
different arguments. Functions are defined in the
same class.
• Function overriding – same function name and
arguments. Defined in different classes.
Compile time and Run time
Polymorphism
• Compile time OR static polymorphism is executed
using function overloading.
• Run time polymorphism or dynamic/late binding is
done using function overriding and virtual functions.
Compile Time Polymorphism Run-Time Polymorphism
At Compile time, which functions to be At Runtime, which function to be
called is decided. called is decided.
Also known as early or static binding Also known as late or dynamic binding
It executes faster because the function is It executes slower because the function
resolved at compilation time only. is resolved at Run-time.
It is achieved through function and It is achieved through function
operator overloading overriding and virtual functions
Default Function
Arguments
• C++ allows a function to assign a parameter a default value when
no argument corresponding to that parameter is specified in a call
to that function.
• For example, this declares myfunc( ) as taking one double
argument
with a default value of 0.0:
void myfunc(double d = 0.0)
{
// ...}
myfunc(198.234); // pass an explicit value
myfunc(); // let function use default
The first call passes the value 198.234 to
d. The second call
automatically gives d the default value
zero.
• The idea behind default argument is simple. If
a function is called by passing argument/s,
those arguments are used by the function.
• But if the argument/s are not passed while
invoking a function then, the default values
are used.
• Default value/s are passed to argument/s in
the function prototype.
Examp
le
Rules for using Default
Arguments
Common Mistakes when using
Default Arguments
Ambiguity in Function
Overloading
• You can create a situation in which the
compiler is unable to choose between two (or
more) overloaded functions.
• When this happens, the situation is said to be
ambiguous.
• Ambiguous statements are and
errors,
programs containing ambiguity will not
compile.
Ambiguity in Function
Overloading
int myfunc(double d);
// ...
cout << myfunc('c');
// not an error, conversion applied
• Although automatic type conversions are
convenient, they are also a prime cause of
ambiguity.
Example
#include <iostream>
using namespace std;
float myfunc(float i);
double myfunc(double i);
int main()
{
cout << myfunc(10.1) << " "; // unambiguous, calls
myfunc(double)
cout << myfunc(10); // ambiguous
return 0;
}
Than
ks