Object Pointers
Just like any ordinary pointer of any primitive data type,
we may have object pointers that point to some object.
Example:
#include<iostream.h>
#include<conio.h>
class complex
{
private:
int a,b;
public:
void getdata()
{
cout<<"Enter real
and img part";
cin>>a>>b;
}
void show()
{
cout<<"\na="<<a<<" b="<<b;
}
};
int main()
{
complex c1;
complex *ptr;
ptr=&c1;
ptr->getdata();
ptr->show();
getch();
}
In the above example,
l We
created an object of type complex and also declared a pointer of type complex.
Name of pointer is ptr which is object pointer as it can point to an object of
type complex.
l Pointer
ptr stores address of object c1.
l When
ever members of object are accessed using object name we need to use dot (.) operator, but if members are to be
accessed through pointer name we need to use arrow (->) operator.
l In
the line ptr->getdata(), function getdata() will work for the object pointed
to by ptr.
Dynamic Memory
allocation: new and delete
Run time memory can be allocated by using keyword new and this allocated memory can be
released using keyword delete.
Example
#include<iostream.h>
#include<conio.h>
void main()
{
clrscr();
int *p= new int;
float *q= new float;
*p=5;
*q=31.45;
cout<<”*p=”<<*p;
cout<<”*q=”<<*q;
delete p;
delete q;
getch();
}
In the above program
l Pointer
p is used to store address of dynamically allocated int type block using keyword
new.
l Pointer
q is used to store address of dynamically allocated float type block using
keyword new.
l In
C language we dynamically allocate memory using predefined functions malloc()
or calloc(), here same functionality is done by keyword new.
l The
only difference between malloc and new is in former case we have to specify the
size of block and in later case we have to mention name of data type.
l Dynamically
allocated memory can be accessed only through pointers.
l Memory
can be released using keyword delete, this is same as function free().
l Notice
that delete releases the memory for specified address. delete p; would not mean that p is released instead it
means delete releases memory pointed to by p.
Example
#include<iostream.h>
#include<conio.h>
void main()
{
clrscr();
int *p= new int[4];
*p=5;
*(p+1)=12;
*(p+2)=33;
*(p+3)=45;
for(int i=0;i<=3;i++)
cout<<”*(p+”<<i<<”)=”<<*(p+i);
delete
[]p;
getch();
}
In the above example
l We
created an array of 4 int blocks dynamically using keyword new.
l Notice
square brackets after data type int. This is used to mention sized of dynamic
array.
l Address
of the first block of array gets stored in p. Subsequent instructions are used
to store values in four blocks of array.
l At
last we use delete to release memory, see its syntax carefully, square brackets
needs to be mentioned before pointer name. It makes possible to release memory
of all four blocks.
Example
#include<iostream.h>
#include<conio.h>
class complex
{
private:
int a,b;
public:
complex() {a=0; b=0;}
void getdata()
{
cout<<"Enter real
and img part";
cin>>a>>b;
}
void show()
{
cout<<"\na="<<a<<" b="<<b;
}
};
void main()
{
clrscr();
complex *p= new complex;
p->show();
p->getdata();
p->show();
delete p;
getch();
}
In the above example
l We
created object pointer to store address of dynamically allocated memory for
object of type complex.
l As
soon as object is created constructor is called.
l p->show() display the values of a and b of this run
time generated object.
l Subsequent
instructions are used to call other members of the class.
l Keyword
delete releases the memory of object pointed to by p.
The this Pointer
l C++
provides a special in-build pointer know as this
pointer.
l this
pointer is a local pointer variable in every member function.
l this
pointer needs no declaration
l Its
scope is limited to the member function.
l Friend
functions are not member functions thus they do not have this pointer.
l Static
member functions do not have this pointer
l this
pointer is of type of that class of which function is a member.
Example
#include<iostream.h>
#include<conio.h>
class
complex
{
private:
int a,b;
public:
void getdata()
{
cout<<"Enter real
and img part";
cin>>a>>b;
}
void show()
{
cout<<"\na="<<a<<" b="<<b;
}
complex sumgreater(complex c)
{
if((a+b) > (c.a+c.b))
return(*this);
else
return(c);
}
};
int
main()
{
complex c1,c2,c3;
c1.getdata();
c2.getdata();
c3=c1.sumgreater(c2);
c3.show();
getch();
}
In the above example
lObserve the member function
sumgreater(), its job to return an object whose sum of member variable is
greater among two objects.
lIn the line c3=c1.sumgreater(c2); sumgreater()
function is called by object c1, thus the address of c1 is passed implicitly to
the member function sumgreater(), which is then received by pointer this.
lWe also passes an object c2 to
function sumgreater(), which is received in object c.
lIf the sum of data stored in a
and b of c1 is greater than the sum of data stored in a and b of c2(
represented by c in sumgreater()) then object c1 has to be returned. The
problem is how to represent caller object c1 in function sumgreater().
lThe problem is solved by using
this pointer, as this contains the address of c1, *this is used to represent
caller object (that is c1 in our example).
Virtual Function
Before going through virtual function we need to understand
few of the basic things.
In inheritance, let us assume there is a Base class and a
Derived class. Now we create a pointer of base class in function main().
Considering this situation dive in for depth knowledge, read the following two
points:
1) Base
class pointer can store address of base class object or can store address of
Derived class object.
2) If
Base pointer contains address of Derived class object, we can access only those
members of Derived class using Base pointer which are inherited from Base class.
Example
#include<iostream.h>
#include<conio.h>
class Base
{
public:
void fun1()
{
cout<<”Base
Class, fun1()”;
}
void fun2()
{
cout<<”Base
Class, fun2()”;
}
};
class Derived : public Base
{
public:
void fun1()
{
cout<<”Derived
Class, fun1()”;
}
void fun2()
{
cout<<”Derived
Class, fun2()”;
}
};
void main()
{
clrscr();
Base *p;
p=
new Base;
p->fun1(); //Base class version is called
p->fun2(); //Base class version is
called
delete p;
p= new Derived;
p->fun1(); //Base class version is called
p->fun2(); //Base class version is
called
delete p;
getch();
}
If we call fun1() or fun2() using Object of Derived class
using dot (.) operator, Derived
class version of functions would be called. This is called function overriding
(see chapter 9)
We focus our discussion to access member using object
pointers only.
In the above example if we made base class version of
function virtual by prefixing a keyword virtual then Base pointer calls version
of function depends on address contained by Base pointer. If Base pointer
contains address of Base class object then function of Base class version would
be run and if Base pointer contains address of Derived class object then
function of Derived class version would run.
Example
#include<iostream.h>
#include<conio.h>
class Base
{
public:
virtual void fun1()
{
cout<<”Base
Class, fun1()”;
}
void fun2()
{
cout<<”Base
Class, fun2()”;
}
};
class Derived : public Base
{
public:
void fun1()
{
cout<<”Derived
Class, fun1()”;
}
void fun2()
{
cout<<”Derived
Class, fun2()”;
}
};
void main()
{
clrscr();
Base *p;
p=
new Base;
p->fun1(); //Base class version is called
p->fun2(); //Base class version is
called
delete p;
p= new Derived;
p->fun1(); //Derived class version is called
p->fun2(); //Base class version is
called
delete p;
getch();
}
Explanation
l A
virtual function is always a member of a class.
l A
function can be made virtual by using keyword virtual (see fun1() in Base
class)
l It
usually has a different functionality in Derived class
l A
function call is resolved at run time
l See
in above example p->fun1() is written two times but they call different
versions, former is a call to Base class version of fun1() and later is call to
Derived class version. Since both the calls look alike it is polymorphism.
l It
is worth mentioning here that virtual function mechanism is valid for functions
sharing same prototype but different definitions, one in Base class and other
in Derived class. If functions have same name but vary in arguments, virtual
keyword losses its effect.
l If
Base class contain any virtual function, it is not mandatory to redefine in
Derived class.
The Virtual table
l To
implement virtual functions, C++ uses a special form of late binding known as
the virtual table. The virtual table is a lookup table of
functions used to resolve function calls in a dynamic/late binding manner. The
virtual table sometimes goes by other names, such as “vtable”, “virtual
function table”, “virtual method table”, or “dispatch table”.
l Every
class that uses virtual functions (or is derived from a class that uses virtual
functions) is given it’s own virtual table.
l This
table is simply a static array that the compiler sets up at compile time.
l A
virtual table contains one entry for each virtual function that can be called
by objects of the class. Each entry in this table is simply a function pointer
that points to the most-derived function accessible by that class.
l The
compiler also adds a hidden pointer to the base class, which we will call
*__vptr.
l *__vptr
is set (automatically) when a class instance is created so that it points to
the virtual table for that class.
l It
makes each class object allocated bigger by the size of one pointer.
l It
also means that *__vptr is inherited by derived classes, which is important.
l When
a class object is created, *__vptr is set to point to the virtual table for
that class. For example, when a object of type Base is created, *__vptr is set
to point to the virtual table for Base.
l When
objects of type Derived is constructed, *__vptr is set to point to the virtual
table for Derived
l When
these virtual tables are filled out, each entry is filled out with the
most-derived function an object of that class type can call.
Virtual Destructor
l Like
constructors, destructors also non inheritable elements of class.
l Remember,
Constructors can not be made virtual, but destructors can be.
l Although
C++ provides a default destructor for your classes if you do not provide one
yourself, it is sometimes the case that you will want to provide your own destructor
(particularly if the class needs to deallocate memory). You should always
make your destructors virtual if you’re dealing with inheritance.
Example (without virtual
destructor)
#include<iostream.h>
#include<conio.h>
class
Base
{
public:
void fun1()
{ cout<<"\nYou are in class
Base and fun1()"; }
~Base()
{ cout<<"You are in Base
Destructor "; }
};
class
Derived: public Base
{
int a,b;
public:
void fun1() //function overriding
{ cout<<"\nYou are in class
Derived and fun1()"; }
~Derived()
{ cout<<"You are in Derived
Destructor "; }
};
int
main()
{
cout<<"You are in main()";
{
Base *p=new Derived;
p->fun1();
delete p;
}
getch();
}
Output:
You are in main()
You are in class Base and fun1()
You are in Base Destructor
l
You can observe that only Base class destructor
executes at the line delete p;
l
To execute Derived class constructor as well as
Base class constructor you need to make destructor virtual.
Example (With Virtual destructor)
#include<iostream.h>
#include<conio.h>
class
Base
{
public:
void fun1()
{ cout<<"\nYou are in
class Base and fun1()"; }
virtual ~Base()
{ cout<<"You are in
Base Destructor "; }
};
class
Derived: public Base
{
int a,b;
public:
void fun1() //function overriding
{ cout<<"\nYou are in
class Derived and fun1()"; }
~Derived()
{ cout<<"You are in
Derived Destructor "; }
};
int
main()
{
cout<<"You are in
main()";
{
Base *p=new Derived;
p->fun1();
delete p;
}
getch();
}
Output:
You are in main()
You are in class Base and fun1()
You are in Derived
Destructor
You are in Base Destructor
Pure Virtual function
A function in a class can be made virtual by writing keyword
virtual and if it has no definition it is called pure virtual function.
Pure virtual function is also known as do-nothing function.
Syntax:
virtual return type function name()=0;
Notice the special way of assigning 0, it is actually not
assignment but just to mention compiler that this function has no body that is
no definition.
If a class is containing pure virtual function:
l Class
is known as abstract class, that is we can not create its object
l To
access member of this class we need to define its derived class since we can
not create its object.
l Derived
class of abstract class must either redefine pure virtual function or
re-declare it again pure virtual function.
Early and Late
Binding
When a program is compiled, the compiler converts each
statement in your C++ program into one or more lines of machine language. Each
line of machine language is given it’s own unique sequential address. This is
no different for functions — when a function is encountered, it is converted
into machine language and given the next available address. Thus, each function
ends up with a unique machine language address.
Binding refers to the process that is used
to convert identifiers (such as variable and function names) into machine
language addresses. Although binding is used for both variables and functions.
Early Binding
Early binding (also called static binding)
means the compiler is able to directly associate the identifier name (such as a
function or variable name) with a machine address.
When the compiler encounters a function call, it replaces the function call
with a machine language instruction that tells the CPU to jump to the address
of the function.Those decisions taken at compile time saves execution time, but such programs are not flexible.
Function overloading and operator overloading are example of early binding.
Late Binding
In some programs, it is not possible to know which function
will be called until runtime (when the program is run). This is known as late
binding (or dynamic binding).
In C++, one way to get late binding is to use function
pointers.
Those decisions taken at run time are taking extra execution
time, but provide greater flexibility to the user.
Virtual function is an example of late binding.
No comments:
Post a Comment