Q-1: Explain The Concept of Constructers and Destructors in C+ + With Programming Examples
Q-1: Explain The Concept of Constructers and Destructors in C+ + With Programming Examples
The class is created. This feature makes it very useful to initialize the
class data members whenever a new object is created. It also can
perform any other function that needs to be performed for all the
object of the class without explicitly specifying it.
Destructors on the other hand are also member functions with the
same name as class but are prefixed with tilde (~) sign to differentiate it
from the constructor. They are invoked automatically whenever the
object’s life expires or it is destroyed. It can be used to return the
memory back to the operating system if the memory was dynamically
allocated. We will see dynamic allocation of memory in unit 7 while
discussing pointers.
// constdest .cpp
# include<iostream.h>
Class sample
Private:
Int data;
Public:
Void display ( )
};
Void main( )
{ sample obj1;
Obj.display ( );
In the above program, the set distance function is replaced with the
two constructor. These are automatically invoked. When object d1 is
created the first constructor distance ( ) is invoked. When object d2 is
created the second constructor distance (int f, int i) is invoked as two
arguments are passed. Please note that to invoke a constructor with
arguments, argument values have to be passed along with the object
name during declaration.
Multiple Inheritance
The derived class can also have multiple parents which is known as multiple inheritance.
Here the child or derived class has two or more parent classes. The child class inherits all
the properties of all its parents. Multiple inheritance is implemented same as single
inheritance except that both the parent names have to be specified while defining the class.
We will discuss multiple inheritance in detail in the next unit.
In the above example we have created a class employee and a student. Manager class is
defined from both of these classes. This is useful in instances when you want to create an
employee whose educational qualifications need not be stored such as a worker.
employee *ptr;
struct node
{int data;
node* next; // pointer to next node
};
class list
{ private:
node *first; // pointer to the first node in the list
public:
list() // no-argument default constructor
{ first=NULL;} // empty list : no first node
void additem(int d) // adds new node to the beginning of the list
{node* newnode; // node pointer
newnode = new node; // create a new node
newnode->data=d; //assign value
newnode->next=first; // assign pointer to the first
first=newnode; // first now points to new node
}void display() // displays all nodes
{ node* temp; // pointer to node
temp=first;
while (temp!=NULL)
{ cout<< endl<<temp->data; // print data
temp=temp->next; // points to next node
}}};
void main()
{ clrscr();
list list1;
list1.additem(25);
list1.additem(50);
list1.additem(52);
list1.display();
getch();
}Every object created has a unique pointer created by default known as this pointer.This
pointer is a default pointer created for every object that points to the object itself. All the member
functions have access to “this pointer”. It is useful to return the object itself after modification. The
following program shows the use of this pointer to overload assignment operator =:
# include<iostream.h>
class alpha
{ private:
int data;
public:
alpha() {data=0;}
When functiong() is called, functionA: : f() is called, although the argument refers to an object
of typeB. At compile time, the compiler knows only that the argument of functiong() will be a
reference to an object derived fromA; it cannot determine whether the argument will be a
reference to an object of typeA or typeB. However, this can be determined at run time. The
following example is the same as the previous example, except thatA: : f() is declared with
thevi rtual keyword:
#include <iostream>
using namespace std;
struct A {
virtual void f() { cout << "Class A" << endl; }
};
struct B: A {
void f() { cout << "Class B" << endl; }
};
void g(A& arg) {
arg.f();
}int main() {
B x;
g(x);
}The following is the output of the above example:
Class BThevirtual keyword indicates to the compiler that it should choose
the appropriate definition off() not by the type of reference, but by the
type of object that the reference refers to.
Therefore, a virtual function is a member function you may redefine for other derived
classes, and can ensure that the compiler will call the redefined virtual function for an object
of the corresponding derived class, even if you call that function with a pointer or reference
to a base class of the object.
A class that declares or inherits a virtual function is called a polymorphic
class.
You redefine a virtual member function, like any member function, in any derived
class. Suppose you declare a virtual function named f in a classA, and you derive directly or
indirectly fromA a class namedB. If you declare a function namedf in classB with the same
name and same parameter list asA::f, thenB::f is also virtual (regardless whether or not you
declareB::f with thevirtual keyword) and it overridesA::f. However, if the parameter lists ofA::f
andB: : f are different,A::f andB: : f are considered different,B :: f does not overrideA::f, andB: : f
is not virtual (unless you have
· The return type ofB ::f must be complete at the point of declaration of
B::f, or it can be of type B.
The following example demonstrates this:
#include <iostream>
using namespace std;
struct A { };
class B : private A {
friend class D;
friend class F;
};
A global_A;
B global_B;
struct C {
virtual A* f() {
cout << "A* C::f()" << endl;
return &global_A;
}};
struct D : C {
B* f() {
cout << "B* D::f()" << endl;
return &global_B;
}};
struct E;
struct F : C {
// Error:
// E is incomplete
// E* f();
};
struct G : C {
// Error:
// A is an inaccessible base class of B
// B* f();
};
int main() {
D d;
C* cp = &d;
D* dp = &d;
A* ap = cp->f();
B* bp = dp->f();
};
B* D::f()
B* D::f()
The statement A* ap = cp->f() callsD: : f() and converts the pointer returned to typeA*. The
statement B* bp = dp->f() callsD::f() as well but does not convert the pointer returned; the type
returned isB*. The compiler would not allow the declaration of the virtual function F ::f()
becauseE is not a complete class. The compiler would not allow the declaration of the
virtual functionG::f() because classA is not an accessible base class of B (unlike friend
classesD andF, the definition ofB does not give access to its members for classG).
A virtual function cannot be global or static because, by definition, a virtual function is a
member function of a base class and relies on a specific object to determine which
implementation of the function is called. You can declare a virtual function to be a friend of
another class.
If a function is declared virtual in its base class, you can still access it directly using the
scope resolution (::) operator. In this case, the virtual function call mechanism is suppressed
and the function implementation defined in the base class is used. In addition, if you do not
override a virtual member function in a derived class, a call to that function uses the function
implementation defined in the base class.
· Defined
· Declared pure
subclasses of the same parent class. Though it is not required, it is understood that the
different methods will also produce similar results (for example, returning values of the
same type).
In practical terms, polymorphism means that if class B inherits from class A, it doesn’t have
to inherit everything about class A; it can do some of the things that class A does differently.
This means that the same “verb” can result in different actions as appropriate for a specific
class, so controlling code can issue the same command to a series of objects and get
appropriately different results from each one.
Polymorphism allows client programs to be written based only on the abstract interfaces of
the objects which will be manipulated (interface inheritance). This means that future
extension in the form of new types of objects is easy, if the new objects conform to the
original interface. In particular, with object-oriented polymorphism, the original client
program does not even need to be recompiled (only relinked) in order to make use of new
types exhibiting new (but interface-conformant) behavior.
(In C++, for instance, this is possible because the interface definition for a class defines a
memory layout, the virtual function table describing where pointers to functions can be
found. Future, new classes can work with old, precompiled code because the new classes
must conform to the abstract class interface, meaning that the layout of the new class’s
virtual function table is the same as before; the old, precompiled code can still look at the
same memory offsets relative to the start of the object’s memory in order to find a pointer to
the new function. It is only that the new virtual function table points to a new implementation
of the functions in the table, thus allowing new, interface-compliant behavior with old,
precompiled code.)
Consider the following example:
#include <iostream>
#include <string>
#include <vector>
using namespace std;
class Animal {
public:
Animal(const string & name) : name(name) { }
virtual ~Animal() { }
virtual const string talk() = 0;
const string name;
};
class Cat : public Animal {
public:
Cat(const string & name) : Animal(name) { }
virtual ~Cat() { }
virtual const string talk() { return "Meow!"; }
};
class Dog : public Animal {
public:
Dog(const string & name) : Animal(name) { }
virtual ~Dog() { }
virtual const string talk() { return "Arf! Arf!"; }
};
// prints the following:
//
// Missy: Meow!
// Mr. Bojangles: Meow!
// Lassie: Arf! Arf!
int main() {
Animal * animals [] = {
new Cat("Missy"),
new Cat("Mr. Bojangles"),
new Dog("Lassie")
};
for(int i = 0; i < 3; i++)
cout << animals[i]->name << ": " << animals[i]->talk() << endl;
for(int i = 0; i < 3; i++)
delete animals[i];
return 0;
}Note that thetalk() method is explicitly declared asvirtual. This is because
polymorphic method calls have relatively high overhead in C++. This overhead is lessened
by treating all method calls as non-polymorphic, unless explicitly marked asvi rtual by the
developer.
4. Write a program in C++ demonstrating various file
handling techniques.
Ans –
#include<iostream.h>
#include<fstream.h>
#include<conio.h>
#include<stdio.h>
#include<process.h>
#include<string.h>
struct student
{ int rollno;
char name[20];
int marks;
}s;
void main()
{ ifstream fin;
ofstream fout;
int ch,no;
do
{ clrscr();
cout<<"\nMenu\n";
cout<<"\n1. Add";
cout<<"\n2. Search";
cout<<"\n3. Display";
cout<<"\n4. Exit";
cout<<"\n\nEnter your choice: ";cin>>ch;
switch(ch)
{ case 1:
{ cout<<"\nEnter rollno. :";cin>>s.rollno;
cout<<"\nName: ";gets(s.name);
cout<<"\nMarks: ";cin>>s.marks;
fout.open("student.dat",ios::binary|ios::app);
fout.write((char*)&s,sizeof(student));
fout.close();
break;
}case 2:
{ cout<<"\nEnter rollno. to be searched: ";cin>>no;
fin.open("student.dat",ios::binary|ios::in);
while(!fin.eof())
{ fin.read((char*)&s,sizeof(student));
if (s.rollno==no)
{ cout<<"\nRollno: "<<s.rollno;
cout<<"\nName: "<<s.name;
cout<<"\nMarks: "<<s.marks;
getch();
break;
}
}fin.close();
break;
}case 3:
{ fin.open("student.dat",ios::binary|ios::in);
while(!fin.eof())
{ clrscr();
fin.read((char*)&s,sizeof(student));
where the exception was thrown.) In addition, only objects that were successfully created at
the time of the exception are destroyed (unlike a normal function return that assumes all the
objects in the scope must be destroyed). Of course, the exception object itself is also
properly cleaned up at the appropriate point. In addition, you can throw as many different
types of objects as you want. Typically, you’ll throw a different type for each different type of
error. The idea is to store the information in the object and thetype of object, so someone in
the bigger context can figure out what to do with your exception.
Catching an Exception
If a function throws an exception, it must assume that exception is caught and dealt with. As
mentioned before, one of the advantages of C++ exception handling is that it allows you to
concentrate on the problem you’re actually trying to solve in one place, and then deal with
the errors from that code in another place.
Thetry block
If you’re inside a function and you throw an exception (or a called function throws an
exception), that function will exit in the process of throwing. If you don’t want athrow to leave
a function, you can set up a special block within the function where you try to solve your
actual programming problem (and potentially generate exceptions). This is called the try
block because you try your various function calls there. The try block is an ordinary scope,
preceded by the keywordtry:
try {
}If you were carefully checking for errors without using exception
handling, you’d have to surround every function call with setup and test code, even if you
call the same function several times. With exception handling, you put everything in a try
block without error checking. This means your code is a lot easier to write and easier to
read because the goal of the code is not confused with the error checking.
Exception handlers
Of course, the thrown exception must end up someplace. This is the
exception handler, and there’s one for every exception type you want to
catch. Exception handlers immediately follow the try block and are
denoted by the keywordca tch:
try {
// code that may generate exceptions
} catch(type1 id1) {
// handle exceptions of type1
} catch(type2 id2) {
// handle exceptions of type2
}// etc…
Each catch clause (exception handler) is like a little function that takes a single argument of
one particular type. The identifier (id1, id2, and so on) may be used inside the handler, just
like a function argument, although sometimes there is no identifier because it’s not needed
in the handler – the exception type gives you enough information to deal with it.
The handlers must appear directly after the try block. If an exception is thrown, the
exception handling mechanism goes hunting for the first handler with an argument that
matches the type of the exception. Then it enters that catch clause, and the exception is
considered handled. (The search for handlers stops once the catch clause is finished.) Only
the matching catch clause executes; it’s not like a switch statement where you need a break
after each case to prevent the remaining ones from executing. Notice that, within the try
block, a number of different function calls might generate the same exception, but you only
need one handler.
common reason for this is that the constructor for the exception object
itself causes a new exception.
terminate()
If an exception is uncaught, the special functionterminate() is automatically called.
Likeunexpected(), terminate is actually a pointer to a function. Its default value is the
Standard C library functionabort(), which immediately exits the program with no calls to the
normal termination functions (which means that destructors for global and static objects
might not be called).
No cleanups occur for an uncaught exception; that is, no destructors are called. If you don’t
wrap your code (including, if necessary, all the code inmain()) in a try block followed by
handlers and ending with a default handler (catch(…)) to catch all exceptions, then you will
take your lumps. An uncaught exception should be thought of as a programming error.
set_terminate()
You can install your ownterminate() function using the standard
set_terminate() function, which returns a pointer to the terminate()
function you are replacing, so you can restore it later if you want. Your customterminate()
must take no arguments and have avoid return value. In addition, anytermina te() handler
you install must not return or throw an exception, but instead must call some sort of
program- termination function. Ifterminate() is called, it means the problem is unrecoverable.
Likeunexpected(), theterminate() function pointer should never be null.
Here’s an example showing the use ofs et_termina te(). Here, the return value is saved and
restored so thetermina te() function can be used to help isolate the section of code where the
uncaught exception is occurring:
//: C07:Trmnator.cpp
// Use of set_terminate()
void (*old_terminate)()
= set_terminate(terminator);
class Botch {
public:
class Fruit {};
void f() {
cout << "Botch::f()" << endl;
throw Fruit();
}~Botch() { throw ‘c’; }
};
int main() {
try{
Botch b;
b.f();
} catch(…) {
cout << "inside catch(…)" << endl;
}} ///:~
The definition ofold_termina te looks a bit confusing at first: It not only creates a
pointer to a function, but it initializes that pointer to the return value ofset_terminate(). Even
though you may be familiar with seeing a semicolon right after a pointer-to-function
definition, it’s just another kind of variable and may be initialized when it is defined.
The classBotch not only throws an exception insidef(), but also in
its destructor. This is one of the situations that causes a call to
terminate(), as you can see in main(). Even though the exception handler
sayscatch(…), which would seem to catch everything and leave no cause fortermina te() to
be called,termina te() is called anyway, because in the process of cleaning up the objects on
the stack to handle one exception, theBotch destructor is called, and that generates a
second exception, forcing a call totermina te(). Thus, a destructor that throws an exception or
causes one to be thrown is a design error.
Standard Exceptions
The set of exceptions used with the Standard C++ library are also available for your
own use. Generally it’s easier and faster to start with a standard exception class than to try
to define your own. If the standard class doesn’t do what you need, you can derive from it.