Module-3-C++
Module-3-C++
Module-3
Constructors, Destructors & Inheritance
Introduction
We have seen, so far, a few examples of classes being implemented. In all the
cases, we have used member functions such as putdata() and setvalue() to
provide initial values to the private member variables. For example, the following
statement
A.input();
invokes the member function input(), which assigns the initial values to the data
items of object A. Similarly, the statement
x.getdata(100,299.95);
passes the initial values as arguments to the function getdata(), where these
values are assigned to the private variables of object x. All these ‘function call’
statements are used with the appropriate objects that have already been
created. These functions cannot be used to initialize the member variables at the
time of creation of their objects.
Providing the initial values as described above does not conform with the
philosophy of C++ language. We stated earlier that one of the aims of C++ is to
create user-defined data types such as class, that behave very similar to the
built-in types. This means that we should be able to initialize a class type variable
(object) when it is declared, much the same way as initialization of an ordinary
variable. For example,
int m = 20;
float x = 5.75;
are valid initialization statements for basic data types.
Similarly, when a variable of built-in type goes out of scope, the compiler
automatically destroys the variable. But it has not happened with the objects we
have so far studied. It is therefore clear that some more features of classes need
to be explored that would enable us to initialize the objects when they are created
3.1 Constructors
A constructor is a ‘special’ member function whose task is to initialize the
objects of its class.
It is special because its name is the same as the class name.
The constructor is invoked whenever an object of its associated class is
created.
It is called constructor because it constructs the values of data members of
the class.
A constructor is declared and defined as follows:
// class with a constructor
class integer
{
int m, n;
public:
integer(void); // constructor declared
.....
.....
};
integer :: integer(void) // constructor defined
{
m = 0; n = 0;
}
not only creates the object int1 of type integer but also initializes its data members
m and n to zero.
There is no need to write any statement to invoke the constructor function
(as we do with the normal member functions).
If a ‘normal’ member function is defined for zero initialization, we would need
to invoke this function for each of the objects separately. This would be very
inconvenient, if there are a large number of objects.
Example Program:
#include<iostream.h>
#include<conio.h>
class Example
{
// Variable Declaration
int a, b;
public:
//Constructor
Example()
{
// Assign Values In Constructor
a = 10;
b = 20;
cout << "I am Constructor\n";
}
void Display()
{
cout << "Values :" << a << "\t" << b;
}
};
void main()
{
Example Object;
// Constructor invoked.
Object.Display();
getch();
}
Output:
I am Constructor
Values :10 20
The constructor functions have some special characteristics. These are the
following:
3.2 Destructors
A destructor, as the name implies, is used to destroy the objects that
have been created by a constructor.
Like a constructor, the destructor is a member function whose name is the
same as the class name but is preceded by a tilde.
For example, the destructor for the class integer can be defined as shown below:
~integer()
{
}
A destructor never takes any argument nor does it return any value.
It will be invoked implicitly by the compiler upon exit from the program (or
block or function as the case may be) to clean up storage that is no longer
accessible.
It is a good practice to declare destructors in a program since it releases
memory space for future use.
Whenever new is used to allocate memory in the constructors, we should
use delete to free that memory.
Example Program:
#include <iostream.h>
#include<conio.h>
int cCount = 0;
int dCount = 0;
class Test
{
public:
// User-Defined Constructor
Test()
{
// Number of times constructor is called
cCount++;
cout << "No. of Object created: " << cCount
<< endl;
}
// User-Defined Destructor
~Test()
{
dCount++;
cout << "No. of Object destroyed: " << dCount
<< endl;
// Number of times destructor is called
}
};
// driver code
int main()
{
Test t, t1, t2, t3;
return 0;
}
Output
No. of Object created: 1
No. of Object created: 2
No. of Object created: 3
No. of Object created: 4
No. of Object destroyed: 1
No. of Object destroyed: 2
No. of Object destroyed: 3
No. of Object destroyed: 4
Inheritance
3.3 Introduction
Reusability is yet another important feature of OOP. It is always nice if we could
reuse something that already exists rather than trying to create the same all over
again.
It would not only save time and money but also reduce frustration and increase
reliability.
For instance, the reuse of a class that has already been tested, debugged and used
many times can save us the effort of developing and testing the same again.
Fortunately, C++ strongly supports the concept of reusability.
The C++ classes can be reused in several ways. Once a class has been written and
tested, it can be adapted by other programmers to suit their requirements. This is
basically done by creating new classes, reusing the properties of the existing ones.
The mechanism of deriving a new class from an old one is called inheritance (or
derivation).
The old class is referred to as the base class and the new one is called the derived
class or subclass.
The derived class inherits some or all of the traits from the base class.
A class can also inherit properties from more than one class or from more than one
level.
A derived class with only one base class, is called single inheritance and one with
several base classes is called multiple inheritance.
On the other hand, multiple derived (child) classes inherit from a single base
(parent) class. This process is known as hierarchical inheritance.
The mechanism of deriving a class from another ‘derived class’ is known as multilevel
inheritance.
Figure shows various forms of inheritance that could be used for writing extensible
programs. The direction of arrow indicates the direction of inheritance. (Some authors
show the arrow in opposite direction meaning “inherited from”.)
The colon indicates that the derived-class-name is derived from the base-class-
name.
The visibility-mode is optional and, if present, may be either private or public.
The default visibility-mode is private. Visibility mode specifies whether the features
of the base class are privately derived or publicly derived.
Examples:
#include<iostream.h>
#include<conio.h>
class B
{
int a; // private; not inheritable
public:
int b; // public; ready for inheritance
void set_ab();
int get_a(void);
void show_a(void);
};
class D : public B // public derivation
{
int c;
public:
void mul(void);
void display(void);
};
//------------------------------------------------------
------------------------------------------------------
void B :: set_ab(void)
{
a = 5; b = 10;
}
int B :: get_a()
{
return a;
}
void B :: show_a()
{
cout << “a = ” << a << “\n”;
}
void D :: mul()
{
c = b * get_a();
}
void D :: display()
{
cout << “a = ” << get_a() << “\n”;
cout << “b = ” << b << “\n”;
cout << “c = ” << c << “\n\n”;
}
//------------------------------------------------------
int main()
{
D d;
d.set_ab();
d.mul();
d.show_a();
d.display();
d.b = 20;
d.mul();
d.display();
return 0;
}
Output:
Output:
a=5
a=5
b = 10
c = 50
a=5
b = 20
c = 100
void D :: display()
{
show_a(); // outputs value of ‘a’
cout << “b = ” << b << “\n”
<< “c = ” << c << “\n\n”;
}
// -------------------------------------------------------
int main()
{
D d;
// d.get_ab(); WON’T WORK
d.mul();
// d.show_a(); WON’T WORK
d.display();
// d.b = 20; WON’T WORK; b has become private
d.mul();
d.display();
return 0;
}
Output :
Enter values for a and b: 5 10
a=5
b = 10
c = 50
where, visibility may be either public or private.. The base classes are separated by
commas.
Example:
class P : public M, public N
{
public:
void display(void);
};
#include<iostream.h>
#include<conio.h>
class M
{
protected:
int m;
public:
void get_m(int);
};
class N
{
protected:
int n;
public:
void get_n(int);
};
class P : public M, public N
{
public:
void display(void);
};
void M :: get_m(int x)
{
m = x;
}
void N :: get_n(int y)
{
n = y;
}
void P :: display(void)
{
cout << “m = ” << m << “\n”;
cout << “n = ” << n << “\n”;
cout << “m*n = ” << m*n << “\n”;
}
int main()
{
P p;
p.get_m(10);
p.get_n(20);
p.display();
return 0;
}
Output:
m = 10
n = 20
m*n = 200
Fig 3.4
4 Hierarchical classification of students
Fig 3.
3.6 Multilevel, multiple inheritance
The result will have both the multilevel and multiple inheritances and its declaration would
be as follows:
class result : public test, public sports
{
.....
.....
};
Below Program illustrates the implementation of both multilevel and multiple inheritance
#include<iostream.h>
#include<conio.h>
class student
{
protected:
int roll_number;
public:
void get_number(int a)
{
roll_number = a;
}
void put_number(void)
{
cout << “Roll No: ” << roll_number << “\n”;
}
};
class test : public student
{
protected:
float part1, part2;
public:
void get_marks(float x, float y)
{
part1 = x; part2 = y;
}
void put_marks(void)
{
cout << “Marks obtained: ” << “\n”
<< “Part1 = ” << part1 << “\n”
<< “Part2 = ” << part2 << “\n”;
}
};
class sports
{
protected:
float score;
public:
void get_score(float s)
{
score = s;
}
void put_score(void)
{
cout << “Sports wt: ” << score << “\n\n”;
}
};
class result : public test, public sports
{
float total;
public:
void display(void);
};
void result :: display(void)
{
total = part1 + part2 + score;
put_number();
put_marks(); Output:
put_score();
Roll No:1234
cout << “Total Score: ” << total << “\n”;
}
Marks Obtained:
int main()
{ Part1=27.5
result student_1;
student_1.get_number(1234); Part2=33
student_1.get_marks(27.5, 33.0);
student_1.get_score(6.0); Sports wt: 6
student_1.display();
return 0; Total Score:66.5
}