|
|||||
CS201
Introduction to Programming
Lecture
Handout
Introduction
to Programming
Lecture
No. 39
Reading
Material
Deitel
& Deitel - C++ How to
Program
Chapter.
5, 6, 7
Revise
old topics
Summary
51)
Pointers
52)
References
53)
Call by
Value
54)
Call by
Reference
55)
Dynamic
Memory Allocation
56)
Assignment
and Initialization
57)
Copy
constructor
58)
Example
59)
Rules
for Using Dynamic Memory
Allocation
60)
Usage of
Copy Constructor
61)
Summary
62)
Exercise
In this
lecture, we will review the
concepts like pointers,
references and memory
allocation,
discussed in the previous
lectures. The review of
these topics will
help
enhance
our understanding of the
programming. Let's discuss
these topics one by
one.
Pointers
Pointer is a
special type of variable that contains a
memory address. It is not a
variable
that
contains a value, rather an
address of the memory that
is contained inside a pointer
variable.
In C++
language, variables can be
without type. Either we can
have a void pointer or
these
can be typed. So we can have
a pointer to an integer, a pointer to a character
and a
pointer to a
float etc. Now we have a
user-defined data type,
which we call classes, so
we
can
have pointers to
classes.
While
talking about pointers, we
actually refer to pointing to an
area in memory. A
pointer to an
integer, points to a location opted by an
integer in memory. If there is
an
Page
497
CS201
Introduction to Programming
array of
integers in the memory, we
can still use a pointer to
point to the beginning of
the
array.
This is a simple manipulation. To
have the address of a memory
location, we use &
sign. The
`&' sign is also used in references.
So we have to be very cautious
while
making
its use. To further overcome
this ambiguity, we will now
recapture the concept
of
reference.
References
A
reference can be considered as a
special type of pointer as it also
contains memory
address.
There are some differences
between pointers and
references. Pointers may
point
to nothing
while references always have
to point to something. A reference is
like an alias
for an
object or a variable. The references
should be used when we are
implementing the
call by
reference. This helps us
make our syntax easier as we
can implement the call
by
reference
with out using the *
operator.
Call by
Value
Whenever
we call a function and pass
an argument, an object or variable to the
function,
then by
the default rule of C and C++, it is a
call by value. It means that
the original data
remains
at its place and a temporary copy of it
is made and passed to the
function.
Whatever
the function does with
this copy, the original
value, in the calling
function,
remains
intact. This is a call by
value.
Call by
Reference
If we
want a function to change something in
the original object variable or
whatever,
that
variable or object by reference would be
passed. To do this, we don't
make
temporary copy of
that object or variable. Rather, the
address of the variable is
sent.
When the
function manipulates it, the
original object will be manipulated,
effecting
change in
its values. The use of
call by reference is also
important for the sake
of
efficiency.
If we have a large object, sending of
its copy will be something insufficient.
It
will
occupy a large space on the
stack. Here, we can use call
by reference instead of
call
by value
only for efficiency while we
need not to change the
original object. For this, we
use a
keyword const
that
means that a const
(constant)
reference is being passed.
The
function
can use its values
but cannot change
it.
Now we
come to the dynamic memory
allocation.
Dynamic
Memory Allocation
In C
language, we have a method to
allocate dynamic memory. In
it, while executing
the
program,
we allocate some memory from
the free store (heap)
according to our need,
use
it and
after using, send it back to
the free store. This,
dynamic memory allocation, is a
very
common function in programming. While
writing C++ language, it was
decided that
it should
not be implemented by a function call.
There should be native
operators,
supposed
to be very efficient. These
operators are new
and
delete. We
allocate memory
with
the new
operator
from the free store. It
returns a pointer. For example, if we say
p
is
a pointer to an
integer, the statement will
be written as
int *p
;
Page
498
CS201
Introduction to Programming
Now we
write
p = new
int ;
This
statement performs the task in
the way that it gets
memory for an integer from
the
free
store. There is no variable name
for that memory location
but the address of
this
location
is stored in the pointer p. Now by
using the syntax *p
(which
means whatever p
points to), we
can manipulate that integer
value. We can also write to
and read from
that
location.
This memory allocation is
done during the execution of the
program. Whenever
we
allocate memory with the
new
operator,
it is our responsibility to de-allocate
this
memory
after the termination of the
program. To do this de-allocation, we
have an
operator
delete. To
de-allocate the memory,
allocated with p = new
int ; we will
write
delete
(p) ;
It will
not delete the p
rather,
it will send the memory
gotten and pointed by p
back to
the
free
store.
Same
thing happens when we try to
allocate more than a simple variable i.e.
when we are
trying to
allocate arrays. When we use
new
operator
to allocate a space for an
array, we
tell
the size of the array in
square brackets (i.e. []). We
can write it like
p = new
int [10] ;
This
statement says p
is
pointing to an area of memory having
the capability to store
10
integers.
So there is an array of 10 integers
whereas p
is
pointing to the beginning
point
of the
array. Now we can access
the elements of the array by
manipulating the pointer p.
To make
the memory allocated for an
array free, the syntax is a
little bit different.
Whenever,
we allocate an array then to
free it we write
delete []
p ;
This
will free the array
that is pointed by p. In this
case, the space of 10
integers that was
pointed
by p,
will be
de-allocated despite the fact
that we write empty brackets
with the
delete
operator.
This is due to the fact that
C++ internally has the
record that how
much
memory
was allocated while allocating an
array. After delete,
the
pointer points to
nothing. So
it's a free pointer and can
be reused.
Now
let's go on and look at a
special type of objects. These
are the objects with
the data
members
as the pointers. In the previous
lectures, we had discussed
the example of a
class
Matrix. In that class, we
said that it will be a two
dimensional matrix while
talking
about
its size etc. In this example, it
will be a generic class
besides being a matrix of 3
x
3. Now we
want to see the Matrix
class defined such a way
that the programmer can
take
an object of it of
any size at any time, say a
matrix of 3 x 3, 5 x 5 or 10 x 10. It
means
Page
499
CS201
Introduction to Programming
that
the size of the matrix
should be variable. Now we have to
bring every thing
together
in terms
of how we declare and manipulate
arrays inside a C++
program.
Whenever
we declare an array, we have to
mention its size. It is necessary, as
otherwise
no memory
will be allocated, it's
about the static memory allocation. In
it, we declare like
that it
will be a two dimensional array of
m
rows
and n
columns.
The compiler will
allocate
a memory for this array at
the start of the program.
Now we are talking about
that
at the
compilation time, we don't
know the size of the array.
We want to allocate an
array
of the
required size at run time. When
the constructor of the class
is called, the memory
required
by that object should be allocated at
that time. For example, in
case of Matrix
class, we
will like to tell the
number of rows and columns of
the object of the Matrix
in
the
constructor, so that the
constructor could allocate
the memory for that object.
A
keyword
new
is
used for this purpose. Here,
we realize that in case of data member of
this
class
Matrix, fixed rows and
columns cannot be used in the
class definition. We
cannot
define a
two-dimensional array inside the
class as it negates the
concept of extensibility.
Here, inside
the class we define something like
int
*m; which
means m
is a
pointer to an
integer.
It is the data member of the
class. In the constructor of
the class, we say that
it
will
take two integer arguments,
rows and columns. Whenever we
declare an object,
which is
an instantiation of the class, the
constructor of the class is called.
Now in the
constructor
of this class we want to
allocate memory from the
free store equal to
the
memory
required by m
x
n
(m
multiply by n) integers. So if we say a
Matrix of 3 rows
and 3
columns then we require memory
for 9 (3 * 3) integers. If we say
four rows and
five
columns then we require a memory
for 20 (4 * 5) integers. Now it is
very simple,
inside
the constructor we will
write
m = new
int [rows * columns]
:
Thus we
created a Matrix to which we
tell the number of rows
and columns, through
variables,
during the execution of the program. When we
created an object of the class
its
constructor
is called to which the variable values
are passed and by
multiplying these
variables
(number of rows and columns), we
allocate a memory for these
integers by the
new
operator
and its address is assigned
to m. Thus
the object is initialized and
the
required
memory is available to it. Now we
can use it as a two dimensional
array and can
put
values inside it. Now the
class definition of the
class Matrix can be written
as under.
class
Matrix
{
private:
int
*m;
int
row, col;
public:
Matrix(int
rows, int cols)
{
m = new
int[rows * cols];
}
Page
500
CS201
Introduction to Programming
};
There is
a requirement that if the
constructor of a class allocates
the memory, it is
necessary
to write a destructor of that
class. We have to provide a destructor
for that
class, so
that when that object ceases
to exist, the memory
allocated by the constructor,
is
returned
to the free store. It is
critically important. Otherwise, when
the object is
destroyed,
there will be an unreferenced
block of memory. It cannot be
used by our
program
or by any other program. It's a
memory leak that should be
avoided. So
whenever
we have a class in which the
constructor allocates dynamic
memory, it is
necessary
to provide a destructor that frees
the memory. Freeing the
memory is an easy
process.
We have no need to remember
that how many rows and
columns were used to
allocate
the memory. We simply use
the delete
operator
with empty brackets and
the
pointer
that points to the allocated
memory. We write this as
follows
delete []
m ;
This
statement frees the
allocated memory (whatever its size
is) that is being pointed
by
m.
Assignment
and Initialization
Let us
discuss the assignment and
initialization. We do initialization
as
int i = 0
;
This is
declaring an integer and initializing
it. The second way to do
this is
int i
;
i=0;
This
has the same effect as the
first statement but the
behavior is different. At first,
a
space is
allocated for i
before
assigning a value to it..
The
same applies whenever we
create an object. We can either
create an object, initialize
it at the
creation time that means
constructor is being called, or we can
create an object
and
then assigns values to its
data members later. This is
usually done either by
set
functions or
with the assignment
statements. Here the thing to
differentiate is that if we
have
two objects of a class, say
Matrix, m1
and
m2. We
have, in some way, created
m1,
its
rows and columns have been
allocated, and values have
been put in these rows
and
columns.
Now somewhere in the program, if we
write
m2 = m1
;
It is an
assignment statement. If we have not
defined the overloaded
operator for
assignment,
the default assignment of C will be
carried out. The default
assignment is a
member-to-member
assignment. Now let's again
look at the construct for
the Matrix
class. In
it, we have only one
data member i.e. a pointer to an integer. We
have written int
*m ;
in
the class definition. So in
m1, there
is a pointer to an integer but m1
is a
fully
Page
501
CS201
Introduction to Programming
qualified
developed object. It is, let's say, a 5 x
5 matrix and has values
for its entities.
Now
when we write
m2 = m1
;
m2
has
also a pointer m
to an
integer as its own data
member. If we have not written
an
overloaded
assignment operator for this
class, the value of m
of
the object m1
will
be
assigned
to m
of
the object m2. Now we
have two objects, having pointer
variables that
hold
the same address. So these
are pointing to the same
region in the memory.
There
arise
many problems with this. Firstly, if we
destroy m1,
the
destructor of m1
is
called
and it
frees the memory. So the
memory being pointed by m
of
the object m1
has
gone
back to
the free store. Now
what happens to m2? There
is a pointer in m2,
pointing to the
same
memory, which has been
sent to the free store by
the destructor of m1. The
memory
is no
longer allocated. It has
been sent to the free
store. So we have a serious
problem.
We don't
want two objects to point to
the same region in the
memory. Therefore, we
write an
assignment operator of our
own. This assignment
operator is such that
whenever
we do
some object assignment, it allocates
separate memory for one
object and copies the
values of
the second object into that
space. Thus the second
object becomes an
independent
object. So we have to be careful. Whenever we
have a class with a
dynamic
memory
allocation, there is need for
writing an assignment operator
for it.
Copy
Constructor
Now we
will see what a copy
constructor is? We have
discussed the dangers that
a
programmer
may face during the process
of assigning the objects.
The same danger
comes to
the scene, when we pass an
object like the Matrix class
to a function that
does
some
manipulations with it. Suppose, we
have a function that takes
an object of a class
Matrix as
an argument. The default mechanism of
calling a function in C or C++ is
call
by value.
Now what is the value
for this object, being
passed to the function? The
values
of the
data members of the object
will be placed on the stack.
The function will get
a
temporary object, as
it is a call by value. The
original object remains intact. The
values of
data
members of that temporary object will be
the same as the values in
the original
object.
Now if it is a simple class,
there will be no problem. However, if
there is a class
with a
pointer as its data member and
that pointer has allocated
some memory, then
the
value of
that pointer is copied. This
value is passed to the
function and not the
memory.
Now in
the function, we have a temporary object
that has a pointer as data
member,
pointing
to the same memory location
as the original object. If we are
just reading or
displaying
that object, there is no problem with
it. If we change the values
of the object,
the
values in the temporary object get
changed. However, when we manipulate
the
pointer, it
changes the values in the
memory of the original object.
This change in the
original
values is the mechanism of the
call by reference and here
we have done call by
value. We
know that a temporary object is passed to
the function. So how we get
around
this
problem? The way to resolve
this problem is to create a complete copy of
the object.
We want
that in this copy of the object
the pointer value must not
point to the same
memory
location. Rather, it must point to the
memory location of its own.
So we want to
have a
copy constructor. That means a
constructor that will create
a new object with a
full
copy of
the other object. This is
also known as deep copy as
opposed to shallow copy.
Page
502
CS201
Introduction to Programming
The
shallow copy makes a member-to-member copy of
the object without taking
care
whether
the pointer is a pointer or an ordinary variable. The
copy of the ordinary
variables
is perfectly valid and legal.
But if we make a member copy of a pointer,
there
may be
the problem in which a new
pointer variable is created with the
same value,
without
creating a new area of
memory. Therefore, we have to
write something special
that is
called copy constructor.
The
basic line in the syntax of
the copy constructor is that we
are trying to create a
new
object by
passing an object of the same
class as the argument. So we
should think of a
constructor.
For example if we have the
class Matrix, its prototype
will be as under.
Matrix
(Matrix &) ;
The
`&' sign shows that a reference of
the object of type Matrix
will be passed. Now
whenever
we write a copy constructor, there is
need to be very cautious. We
have to
allocate
a new memory for that copy.
When we go into this copy constructor, at
first, it is
determined
that how much memory the
original object has allocated?
Since we pass the
object, so
all the queries might
have answers. For example, in
case of Matrix class,
we
can
find the number of rows
and columns. We create a temporary object inside
the
constructor
and allocate it a memory.
Then the values of memory of
the original object
are
copied into this memory. Now
the pointer of this new object
will point to this
new
location. As
the constructor returns nothing
and just creates a new
object, so there is no
return
statement in the constructor.
Thus, this copy constructor is completed.
The syntax
of this
constructor is given below
Matrix::Matrix
( const Matrix &other )
{
size =
other.size ;
// size is a
function to
determine
the
memory
allocated by object
m = new
int [size] ;
copyvalues
( m, other ) ;
}
In this
case, it creates a new object
that actually creates
memory. It does not make
the
shallow
copy so there is no copy of the pointer
value. In fact, the pointer has a
new value.
But
the values pointed to by this pointer
(i.e. the values in the new
allocated memory) are
the copy
of the values in the memory
of the original object, and
then this fully
constructed
object is returned.
Now we
have the facility of copy
constructor. With it, we can
define new objects
based
on the
existing objects. This copy
constructor is necessary for
the objects in which
the
memory is
allocated dynamically. We can
use this copy constructor
without causing any
problems.
Suppose we have written
as
Matrix a
(3,3) ;
Page
503
CS201
Introduction to Programming
We have
defined a constructor that takes
two arguments rows and
columns, as a matrix
`initializer'.
We allocate memory for these
rows and columns and create
an object. Then
somewhere,
later in the program, We
write
Matrix b
(a) ;
This
statement makes a complete copy of
already created object a
and a
new object b
is
created.
This new object has its
own pointer and own memory
allocation. This memory
location
is different from the memory
allocated inside the matrix
a. Now if
a
dies
then b
is still
a valid object. So a copy constructor is
critically useful. It is used when we
want to
create a
duplicate copy of an object. It is always used
whenever we want to do a call
by
value
into a function to which an object is
being passed, and the object
is of a class in
which we
have allocated the memory
dynamically.
Example
Let's
look at an example to further understand
these concepts. We write a
class String.
It
has a
data member c
that is a
pointer to a character. We write a member
function (copy
function)
of the class, which can copy
values in the character
array of the class. There is
a
constructor
that will allocate a space
for the character arrays
i.e. string. The starting
address
of this array will be stored
in data member of the class i.e. in a
pointer. We have
not
written any copy constructor.
Now we want to make two
objects of this String
class,
say,
s1
and
s2. We
create object s1
and
assign a string to it. We write it
as
String s1
("test1") ;
Now
after this we write
String s2
= s1 ;
Thus we
create a new object s2. The
values of s2
are
initialized with the values
of s1.
As
we have
written no copy constructor, C will
provide the default copy constructor. Now
if
we
display the string of s2, it will
be the same as of s1. Now
use the copy function
to
assign
new values to the string inside
the object s1. So we
write
s1.copy("A
new string") ;
Thus we
write a new string in s1. Now
again if we display the string of s2
by
writing
s2.print
;
We will
see that it displays the
same value, assigned to s1
in
the previous statement.
The
reason is
that the default copy constructor
has done member-to-member copy. It
has
copied
the value of the character
pointer to the pointer of s2
and
thus both pointers
are
pointing
to the same memory
location.
Page
504
CS201
Introduction to Programming
Now
there is need of providing a copy
constructor for a class like
this. We also have to
provide a
destructor as we are doing memory
allocation inside the constructor of
the
class.
Following
is the code of the example in
which we provide a copy constructor. We
create
an object
based on an existing object. The copy
constructor creates an object with
full
copy of
the existing object with its
values in a new memory
location.
/*This
program has a copy constructor
and demonstrate the use of
it.
We create
a new object by passing it an existing
object, this calls
the copy
constructor and thus creates
a complete copy of the passing
object,
and has its values in
new location of
memory.
*/
#include
<iostream.h>
#include
<stdlib.h>
// class
definition
class
String
{
char
*c;
public:
// copy
function
void copy
(char *s)
{
c=s;
}
// getting
the length of the string
int
length ()const
{
return
strlen(c);
}
//constructors
String
();
String
(const char *s)
{
c = new
char [ 30 ];
strcpy
(c, s);
}
// copy
constructor
String(
const String &other );
//display
the string
void
print()
{
cout
<< c << endl ;
Page
505
CS201
Introduction to Programming
}
//destructor
~String()
{
delete
[]c ;
}
};
//
definition of copy constructor
String::String(
const String &other )
{
int
length;
length =
other.length();
c = new
char[length + 1];
strcpy( c,
other.c );
}
main
()
{
String
s1("test1");
cout
<< "The string of s1 is " ;
s1.print();
String
s2(s1);
cout
<< "The string of s2 is " ;
s2.print();
s1.copy("A
new string"); // assign new
value to string s1
cout
<< "The string of s1 is " ;
s1.print();
cout
<< "The string of s2 is " ;
s2.print();
//s2
has its own previous
value
}
The
following is the output of the
program which shows the
use of copy constructor.
The
string of s1 is test1
The
string of s2 is test1
The
string of s1 is A new string
The
string of s2 is test1
The
other affected part is the
assignment operator itself. We
know that there are
dangers
in the
assignment operators of a class in
which memory is being
allocated. We cannot
write an
assignment operator for such
a class blindly. When we write an
assignment
operator
for such a class, that
operator must first look at
the fact whether there is
self-
assignment
being done.
Suppose
we have an integer i. We have
written as
int i
;
i = 10
;
Page
506
CS201
Introduction to Programming
And
down to this we write
i=i;
There is
no problem with it. It is a legal
statement. It is complicated if we do
such
assignment
through pointers. In such a
case, pointer is pointing to itself
and even it has no
problem.
But when we do this with
objects that have allocated
dynamic memory, the
method of
assignment is changed. Let's
take the example of Matrix. We
have an object of
Matrix,
say m1, which
has three rows and
three columns. Another object, say
m2, has
five
rows
and five columns. Somewhere in
the program we write
m1 = m2
;
Here m2
is a
big object while m1 is a small
one. We want to assign the
big object to the
smaller
one. The assignment operator
for this type of class,
first de-allocates the
memory
reserved
by the left-hand side object. It frees
this by the delete
operator.
Then it will
determine
the memory required by the
object on right hand side. It
will get that
memory
from
the free store by using
new
operator.
When it gets that memory, it
will copy the
values
and thus the statement
m1 =
m2;
becomes effective. So assignment
has a
requirement.
Now if we
say
m1 = m1
;
We have
defined assignment operator.
This operator will delete
the memory allocated
by
m1
(i.e.
object on L.H.S.). Now it wants to
determine the memory
allocated by the object
on the
right hand side, which in
this case, is the same i.e.
m1. Its
memory has been
deleted.
So here we get a problem. To avoid such
problem, whenever we write an
assignment
operator, for objects of the
class that has done
memory allocation. After this,
we do
other things.
We have
discussed the example in which we
create an object of Matrix. We create
it
using a
copy constructor by giving it another
object. The syntax of it we have written
as
Matrix m2
(m1) ;
This is
the syntax of creating an object based on
an existing object. We can write it in
the
following
fashion.
Matrix m2
= m1 ;
While
this statement, we should be
very clear that it is not an
assignment only. It is also
a
construction. So
whenever we are using initialization,
the assignment operator
seems as
equal to
operator. But actually
assignment operator is not called.
Think about it
logically
that
why assignment operator is
not called? The assignment
operator is called for
the
existing
objects. There should be
some object on the left-hand side,
which will call
the
assignment
operator. When we have written
the declaration line
Matrix m2
= m1 ;
The m2
object has not constructed yet.
This object of Matrix does
not exist at the time
of
writing
this statement. So it cannot be calling
the assignment function or
assignment
operator.
This is an example of the use of a copy
constructor. Thus, there are
two
Page
507
CS201
Introduction to Programming
different
ways to write it. Remember
that whenever we create an object
and initialize it in
the
declaration line, it calls the copy
constructor.
Let's
talk about another danger
faced by the programmers
when they do not provide
copy
constructor.
The ordinary constructor is there
which allocates memory for
the objects of
this
class. Suppose we do a call by
value to a function. Here, we know
that a temporary
copy of
the object is made and provided to
the function. The function
does manipulations
with this
copy. When the function returns
that temporary copy is destroyed. As no
copy
constructor
is there, a shallow copy, with
values of pointers, is made.
The destructor
should be
there as we do memory allocation in the
class. Now suppose that
there is a
destructor
for that class. Now
when this temporary object destroys
its destructor
executes
and
de-allocates the memory. Now
as it was a shallow copy so its pointers
were pointing
to the
same memory as of the
original object. In this way,
actually, the memory of
the
original
object is de-allocated. So the pointer of
the original object now points to
nothing.
Thus, in
the process of function
call, we destroyed the
original object as it is an
invalid
object
now. Its pointer is pointing to an
unknown memory location. This is a
subtle but
very
critical. This can be
avoided by providing a copy constructor,
which actually
constructs
a fully formed object with
its own memory. That
temporary object will go to
the
function . When it is destroyed, its
destructor will de-allocate
this memory. However,
the
original object will remain the
same.
Rules
for Using Dynamic Memory
Allocation
Whenever
we have a class in which we do
dynamic memory allocation, there
are some
rules
that should be
followed.
First, we must define
a constructor for it. Otherwise, we
will not be able to carry
out
dynamic
memory allocation. This constructor
should be such that it gets
memory from
the
free store, initializes the
object properly, sets the value of
the pointer and returns
a
fully
constructed object.
Secondly, we must
write an assignment operator
for that class. This
assignment operator
should
first check the
self-assignment and then
make a deep copy.. So that a
properly
constructed
object should be achieved..
Thirdly,
as we are doing dynamic memory allocation
in the constructor, it is necessary
to
provide a
destructor. This destructor
should free the allocated
memory.
These
three rules are must to
follow.
Usage of
Copy Constructor
Let us
see where the copy constructors
are being used?
First, it is
used explicitly at some
places, where we write
Matrix m2
(m1) ;
Page
508
CS201
Introduction to Programming
This is
an explicit call to a copy constructor.
Here m1
is
being passed by reference. If
we
want
that there should be no
change in m1, then it
is necessary to use the key
word const
with it
to prevent any change in m1. The
presence of this key word
means that the
constructor
will do nothing with the object,
being passed to it. The
use of copy
constructor
in this explicit way is
clear.
The
second way to use the copy
constructor is by writing the
declaration line as
Matrix m2
= m1 ;
Here the
use of copy constructor is not
clear. It is not clear by
the statement that
copy
constructor
is being used. It seems an
assignment operator is being
used. Be careful that it
is not an
assignment operator. It is a copy
constructor.
The
third use of the copy
constructor is calling a function
and passing it an object by
value. If
we have provided a copy constructor, it
will be called automatically and
a
complete temporary
copy (with memory allocation) of
the object is given to the
function.
If we do
not provide copy constructor, the
call by value functions will
create problems. In
the
function, if we change any
value of the object, it will
change the value in the
original
object.
In
function calling, when we do
the call by value, the copy
constructor is called. On the
other
hand, in call by reference, copy
constructor is not called and
the address of the
original
object is passed.
Summary
A pointer is a
variable that holds memory
address. The & operator is
used to get the
address
of a variable or an object. The & sign is also
used as a short hand for a
reference.
Whenever
we have a & sign in the declaration,
it implies a reference. Whenever we
have
& sign on
right hand side of an
assignment operator, it is taken as
address of an object.
We can do
dynamic memory allocation by using
pointers.
In C++
language, we have two very
efficient operators provided which
are new
and
delete. We use
the new operator for
obtaining memory from the
free store. It returns
a
pointer to
the allocated memory. We
store the value of this
pointer in a pointer variable.
In a
class, which allocates
memory dynamically, there is a
data member i.e. a pointer.
When we
create an object of the class at
run time, it will allocate
memory according to
our
requirement. So there is no waste of
memory and the situations in
which we want to
store
large data in small memory
or vice versa are prevented.
So we do dynamic memory
allocation inside
these classes.
Whenever
we have dynamic memory allocation inside
a class, we have to provide
few
things. We must
provide a constructor that does
the memory allocation for us producing
a
well-formed
object.
We must provide a copy
constructor that is able to
create fully formed copies
of the
objects.
That means is should not
only make the copies of
the values of the pointers
but it
should
give the pointers new
values by allocating new memory
for the object. And
should
copy the
values of the original object
into this new memory
location.
Page
509
CS201
Introduction to Programming
We must provide an
assignment operator. This
operator should be able to
check the self-
assignment
and can assign one object to
the other in a proper fashion
using the concept of
deep copy
and not a shallow copy. So we
allocate memory space then
copy element by
element
in this allocated
memory.
And
finally we must do de-allocation which
means whenever we destroy an object
and it
goes
out of scope, we should free
the memory allocated by that
object. To do the memory
de-allocation we must
provide the destructor of the
class in which we free
(delete) the
memory by
using deletes
operator.
Exercise
You
should write small programs
to examine the working of these
rules. You can
check
this if
we allocate memory and do
not delete it in the
destructor. Then the next
time,
when we
execute the program it will
allocate a new memory. We
can find that
which
memory is
assigned by displaying the
value of the pointer (not the
value it points too). It
will be a
number with 0x-notation i.e. it
will be in hexadecimal. We don't care
about the
exact
value but we will find that
if we have provided a proper destructor.
Then on the
same
computer, in the same session, we
execute the program, a specific
address of
memory
will be assigned to the
program. With the proper
destructor, we stop the
program
and
then again start it.
Nine out of ten times, we get
the same memory. That
means we
will
see the same address.
Nine times out of ten is
because the operating system
can use
this
memory somewhere else
between the times of two
executions of the program. If we
do not
provide a destructor i.e. we do not
deallocate the memory, it is
necessary that each
time we
will get a new memory.
The previous memory is being
wasted. You can prove
it
by
yourselves by writing small
programs.
Page
510
Table of Contents:
|
|||||