0% found this document useful (0 votes)
5 views78 pages

PPT8

The document discusses various concepts of inheritance in C++, including constructors and destructors in base and derived classes, the use of virtual classes, abstract classes, and pure virtual functions. It explains how constructors are called in order of derivation and how virtual base classes can prevent ambiguity in multiple inheritance scenarios. Additionally, it touches on polymorphism, detailing compile-time and run-time polymorphism, and the importance of pure virtual functions in defining abstract classes.

Uploaded by

abhigyans604
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
5 views78 pages

PPT8

The document discusses various concepts of inheritance in C++, including constructors and destructors in base and derived classes, the use of virtual classes, abstract classes, and pure virtual functions. It explains how constructors are called in order of derivation and how virtual base classes can prevent ambiguity in multiple inheritance scenarios. Additionally, it touches on polymorphism, detailing compile-time and run-time polymorphism, and the importance of pure virtual functions in defining abstract classes.

Uploaded by

abhigyans604
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 78

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> { cout << "Destructing derived\n"; }
Using namespace std; };
//base class int main()
class base { {
public: derived ob;
base() // do nothing but construct and
{ cout << "Constructing base\n"; } destruct ob
return 0;
~base()
{ cout << "Destructing base\n"; } }
};
//derived class
class derived: public base { Program Output
public: Constructing base
Constructing derived
derived()
Destructing 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 derived1 {
#include <iostream> public:
using namespace std; derived2() { cout <<
class base { "Constructing derived2\n"; }
public: ~derived2() { cout <<
"Destructing derived2\n"; }
base() };
{ cout << "Constructing base\n"; int main()
}
~base() { cout << "Destructing {
base\n"; } derived2 ob;
}; // construct and destruct ob
class derived1 : public base { return 0;
public: } Program Output:
derived1() { cout << Constructing base
"Constructing derived1\n"; } Constructing derived1
~derived1() { cout << Constructing derived2
"Destructing derived1\n"; } Destructing derived2
}; Destructing derived1
Destructing base
6
//MULTIPLE BASE CASES class derived: public base1, public
#include <iostream> base2 {
using namespace std; public:
class base1 { derived() { cout << "Constructing
derived\n"; }
public: ~derived() { cout << "Destructing
base1() { cout << "Constructing derived\n"; }
base1\n"; } };
~base1() { cout << "Destructing int main()
base1\n"; }
}; {
class base2 { derived ob;
public: // construct and destruct ob
base2() { cout << "Constructing return 0;
base2\n"; } } Program Output:
Constructing base1
~base2() { cout << "Destructing Constructing base2
base2\n"; } Constructing derived
}; 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
#include <iostream> along to base.
using namespace std; derived(int x, int y): base(y)
class base { { j=x;
protected: cout << "Constructing
derived\n"; }
int i; ~derived() { cout << "Destructing
public: derived\n"; }
base(int x) void show() { cout << i << " " << j
<< "\n"; }
{ i=x; cout << "Constructing
base\n"; } };
~base() { cout << "Destructing int main()
base\n"; } {
}; derived ob(3, 4);
class derived: public base { ob.show(); // displays 4 3
int j; return 0;
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
base1(int x) { i=x; cout << derived\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
base1\n"; } }; return 0; }
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
ambiguous, which i???
using namespace std;
ob.j = 20;
class base {
ob.k = 30;
public: int i; }; // i ambiguous here, too
class derived1 : public base { ob.sum = ob.i + ob.j + ob.k;
public: int j; }; // also ambiguous, which i?
class derived2 : public base { cout << ob.i << " ";
public: int k; }; cout << ob.j << " " << ob.k ;
class derived3 : public
cout << ob.sum;
derived1, public derived2 { 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 +
class derived2 : public base { ob.k;
public: int k; }; // also resolved here
class derived3 : public cout << ob.derived1::i << " ";
derived1, public derived2 { cout << ob.j << " " << ob.k << " ";
public: int sum; }; cout << ob.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
// derived2 inherits base as virtual. ob.sum = ob.i + ob.j + ob.k;
// unambiguous
class derived2 : virtual public base { cout << ob.i << " ";
public: int k; };
cout << ob.j << " " << ob.k << " ";
class derived3 : public derived1, public cout << ob.sum;
derived2
{ public: int sum; }; return 0;
}
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
Contents
• Polymorphism
• Function Overloading
• Default Function Arguments
• Ambiguity in Function Overloading
Polymorphism
• 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;
}
Example 2
#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.
Example 3
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 It executes slower because the function
is 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.
Example
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 errors, and
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;
}
Thanks

You might also like