| 
 | 
 
 
 
 | 
 
  | 
  
 
 
 Summary 
	 
• Arrays of Objects  
• Dynamic Arrays of Objects  
• Overloading new and delete Operators 
 
• Example of Overloading new and delete as Non-members 
 
• Example of Overloading new and delete as Members 
 
• Overloading [] Operator to Create Arrays  
 
	Arrays of Objects
 
A class is a user-defined data type. Objects are instances of classes the way
int variables
 
are instances of ints. 
Previously, we have worked with arrays of 
ints. Now, we are going
 
to work with arrays of objects.  
The declaration of arrays of user-defined data types is identical to the array of
primitive  
data types.  
Following is a snapshot of our veteran 
Date class: 
 
/* Snapshot of Date class discussed in previous lectures */  
class Date  
{  
private:  
int day, month, year;  
public:  
/* Parameterless constructor, it is created by the compiler automatically when we
 
don’t write it for any of our class. */  
Page 433  
Date( )  
{  
cout << "\n Parameterless constructor called ...";  
month = day = year = 0;  
}  
/* Parameterized constructor; has three 
ints 
as parameters. */  
Date(int month, int day, int year)  
{  
cout << "\n Constructor with three int parameters called ...";  
this->month = month;  
this->day = day;  
this->year = year;  
}  
~Date ( )  
{  
cout << "\n Destructor called ...";  
}  
. . .  
. . .  
};  
Consider the example of declaring an array of 
10 date objects of
Date class. In this 
case,  
the declaration of arrays will be as under:  
Following is the declaration: 
 
Date myDates [10] ;  
With this line (when this line is executed), we are creating 10 new objects of
Date class.  
We know that a constructor is called whenever an object is created. For every object 
like  
myDate[0], myDate[1],…. myDate[9], the constructor of the 
Date class is called.
 
Theimportant thing to know here is that which constructor of Date class is being 
called to  
construct objects of the array 
myDates. As we are not 
doing any initialization of the array  
objects explicitly, the default constructor (parameterless constructor) of the Date 
class is  
called. Remember, the default constructor is defined by the C++ compiler automatically
 
for every class that has no parameterless constructor defined already. In our case 
of Date  
class, we have defined a parameterless constructor, therefore, the compiler will 
not  
generate default constructor automatically.  
We can also initialize the array elements at the declaration time. This initialization 
is  
similar to that done for native data types. For 
int array, we used to do 
initialization in the  
following manner:  
int array [10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 } ;  
Similarly, we initialize Date
array while declaring it: 
 
Page 434  
Date yourDate [3] = { Date(10, 24, 1980), Date(06, 14, 1985), Date(07, 09,1986) 
};  
The above statement will call the parameterized constructor 
Date (int month, int day, int  
year) of Date
class to create three objects of 
myDate array. This parameterized
 
constructor carries out initialization of the objects data member (month, 
day, year) with  
the values supplied as arguments to it.  
It will be interesting to know, how the following statement works: 
 
Date myDate [10] = { Date(09, 03, 1970), Date(08, 23, 1974) } ;  
We are trying to declare an array of 10 Date objects while supplying only initialization
 
values for the first two elements. At first, we might be doubtful if the statement 
is  
compiled successfully. Not only it compiles successfully but also does the initialization
 
of the first two objects ( 
myDate[0], myDate[1] ). What will happen to the remaining  
objects in the array? Actually, all the 
10 objects are created 
successfully by the above  
statement. The parameterized constructor is called for the first two objects (
myDate[0],  
myDate[1] ) and parameterless constructor is called for the remaining 
objects  
(myDate[2], myDate[3], …, myDate[9]).
 
You might have noticed that at the array initialization stage, we have explicitly 
called  
parameterized constructor of 
Date for every object. We may specify only the argument  
when a constructor with only one parameter is called.  
/* A snapshot of String class discussed in previous lectures */  
class String  
{  
private :  
char *buf ;  
public:  
// Constructors  
String ();  
String( const char *s )  
{  
buf = new char [ 30 ];  
strcpy (buf,s);  
}  
. . .  
. . .  
};  
Page 435  
For example, in the above-mentioned case of 
String class, we have a 
constructor that is  
accepting one argument of type 
char *. While writing our 
code, we can declare and  
initialize an array of Strings 
as follows:  
String message [10] = { "First line of message\n",  
"Second line of message\n",  
String( "Third line of message\n"),  
String( )  
};  
See the initializing arguments for first two objects i.e, (message[0], 
message[1]) in the  
array. Here only one string is being passed. Therefore, for the first two objects,
 
constructor with one parameter of type 
char * of 
String class is called 
automatically. That  
constructor is String ( char 
* str ). For the third object (message[2]), 
the same  
constructor with one char *
as parameter is being called explicitly. For fourth object  
(message[3]), parameterless 
constructor i.e., String ( 
) is being called explicitly, though,  
this was optional as parameterless constructor is called up automatically when no
 
initialization is made. As there is no explicit initialization for the remaining 
six objects,  
the parameterless constructor is called up automatically.  
Can we create arrays of objects dynamically? As usual, the answer is yes. Let’s 
discuss it  
in detail.  
Dynamic Arrays of Objects
 
Consider the following statement:  
1. String *text ; 
 
2. text = new String [5] ;
 
In line 1, we have declared a pointer 
text of 
String type.  
In line 2, we are creating an array of 
5 objects of 
String type. This statement 
allocates  
space for each object of the array, calls the parameterless constructor for each 
object and  
starting address of the first object is assigned to the pointer 
text.  
The important point to be noted here is that in line 2, we can’t initialize objects 
because  
there is no way to provide initializers for the elements of an array allocated with
new.  
The default constructor (parameterless constructor) is called for each element in 
the array  
allocated with new. 
Remember, the default constructor for a class is generated by C++  
compiler automatically if it is not defined already in the class definition.  
To deallocate these arrays of objects, the 
delete operator is used 
in the same way as it is  
used for the native data types.  
There are few cautions that should be taken care of while performing these operations 
of  
allocation and deallocation with arrays of objects.  
Firstly, while deallocating an array allocated with 
new operator, it is important 
to tell the  
compiler that an array of objects is being deleted. The brackets ( 
[] ) are written in our
 
delete statement after the 
delete keyword to inform 
the delete operator 
that it is going to  
Page 436  
delete an array. The consequences of using the wrong syntax are serious. For example, 
if  
we want to delete previously created array of five 
String objects using the 
following  
statement:  
delete text; // Incorrect syntax of deleting an array  
The delete operator in this case will not be aware of deleting (deallocating) an 
array of  
objects. This statement will call the destructor only for the object pointed by 
the text
 
pointer i.e. String[0] 
and deallocate the space allocated to this object. The requirement is  
to call the destructor for all the objects inside the array and deallocate the space 
allocated  
to all of these objects. But on account of the wrong syntax, only the first object 
is deleted  
and the remaining four objects ( 
String[1], String[2], String[3], String[4]
pointed by  
text[1], text[2], text[3], text[4] respectively ) remain in the memory 
intact. The memory  
space occupied by these four objects results in 
memory leak as the same 
program or any  
other program on the same computer cannot use it unless it is deallocated.  
Calling the destructor while destroying an object becomes essential when we have
 
allocated some memory in free store from inside the object (usually from within 
the  
constructor).  
To destroy an array of objects allocated on free store using the 
new operator, an array
 
equivalent of delete 
operator is used. The array equivalent of 
delete operator is to write
 
empty square brackets after the 
delete keyword (delete 
[] ). So the correct statement is: 
 
delete [] text ;  
This statement destroys the whole array properly. It calls destructor for each object 
inside  
the array and deallocates the space allotted to each object. Actually, by looking 
at the  
brackets ( [] ) 
after delete, the 
compiler generates code to determine the size of the array  
at runtime and deallocate the whole array properly. Here, it will generate code 
to  
deallocate an array of 5
objects of String
type.  
If we create an array of Date
objects and want to delete them without specifying array  
operator: It will look as under: 
 
// Bad Technique: deleting an array of objects without []  
// for a class that is not doing dynamic memory allocation internally  
Date * ppointments;  
appointments = new Date[10];  
. . .  
delete appointments; // Same as delete [] appointments;  
Although, this is good to deallocate an array of objects without specifying array 
operator  
([]) as there is no dynamic memory allocation occurring from inside the 
Date class. But  
this is a bad practice. In future, the implementation of this class may change. 
It may  
contain some dynamic memory allocation code. So it is always safer to use array 
operator  
( [] ) to delete 
arrays.  
Can we overload new 
and delete 
operators? Yes, it is possible to overload 
new and 
delete  
operators to customize memory management. These operators can be overloaded in
 
global (non-member) scope and in class scope as member operators. 
 
Page 437  
Overloading of new and delete Operators
 
Firstly, we should know what happens when we use 
new operator to create 
objects. The  
memory space is allocated for the object and then its constructor is called. Similarly,
 
when we use delete 
operator with our objects, the destructor is called for the object before
 
deallocating the storage to the object.  
When we overload new 
or delete 
operators, it can only lead to a change in the allocation  
and deallocation part. The call to the constructor after allocating memory while 
using new  
operator and call to the destructor before deallocating memory while using
delete  
operator will be there. These calls to constructors and destructors are controlled 
by the  
language itself and these cannot be altered by the programmer.  
One of the reasons of overloading 
new and 
delete operators can be 
their limited current  
functionality. For example, we allocate space on free store using the 
new operator for 
 
1000 ints. It will fail and return 
0 if there is no contiguous 
space for 1000 ints 
in free  
store. Rather the free store has become fragmented. The total available memory is 
much  
more than 1000 ints 
, but in fragments. There is no contiguous segment of at least 
1000  
ints. The built-in 
new operator will fail to get the required space but we can overload 
our  
own new operator 
to de-fragment the memory to get at least 
1000 ints space. Similarly,
 
we can overload delete 
operator to deallocate memory.  
In the embedded and real-time systems, a program may have to run for a very long 
time  
with restricted resources. Such a system may also require that memory allocation 
always  
takes the same amount of time. There is no allowance for heap exhaustion or  
fragmentation. A custom memory allocator is the solution. Otherwise, programmers 
will  
avoid using new 
and delete altogether 
in such cases and miss out on a valuable C++ asset.  
There are also downsides of this overloading. If we overload 
new or 
delete operator at  
global level, the corresponding built-in 
new or 
delete operator will not 
be visible to whole  
of the program. Instead our globally written overloaded operator takes over its 
place all  
over. Every call to new 
operator will use our provided 
new operator’s implementation.
 
Even in the implementation of 
new operator, we cannot use the built-in 
new operator.  
Nonetheless,when we overload 
new operator at a class level then this implementation of 
 
new operator will be visible to only objects of this class. For all other 
types (excluding  
this class) will still use the built-in 
new operator. For example, 
if we overload new 
 
operator for our class Date
then whenever we use 
new with 
Date, our overloaded
 
implementation is called. 
 
Date* datePtr = new Date;  
This statement will cause to call our overloaded 
new operator. However, 
when we use  
new with any other type anywhere in our program as under: 
 
int* intPtr = new int [10];  
The built-in new 
operator is called. Therefore, it is safer to overload 
new and 
delete  
operators for specific types instead of overloading it globally. 
 
Page 438  
An important point to consider while overloading 
new operator is the return 
value when  
the new operator 
fails to fulfill the request. Whether the operator function will return 
0 or  
throw an exception.  
Following are the prototypes of the 
new and 
delete operators:
 
void * operator new ( size_t size ) ;  
void operator delete ( void * ptr) ;  
The new operator 
returns a void * 
besides accepting a parameter of whole numbers 
size_t.  
This prototype will remain as it is while overloading 
new operator. In the implementation
 
of overloaded new 
operator, we may use 
calloc( ) or 
malloc( ) for memory allocation and  
write some memory block’s initialization code.  
The delete operator returns nothing (void) 
and accepts a pointer of void 
* to the memory  
block. So the same pointer that is returned by the 
new operator, is passed 
as an argument  
to the delete operator. 
Remember, these rules apply to both if operators (new
and delete)
 
are overloaded as member or non-member operators (as global operators). Importantly,
 
whenever we use these operators with classes, we must know their sequence of events
 
that is always there with these operators. For 
new operator, memory block 
is allocated  
first before calling the constructor. For 
delete operator, destructor 
for the object is called  
first and then the memory block is deallocated. Importantly, our overloaded operators 
of  
new and delete
only takes the part of allocation and deallocation respectively and calls 
to  
constructors and destructors remain intact in the same sequence.  
Because of this sequence of events, the behavior of these 
new and 
delete operators is  
different from the built-in operators of 
new and 
delete. The overloaded
new operator  
returns void * when 
it is overloaded as non-member (global). However, it returns an  
object pointer like the built-in 
new operator, when overloaded 
as a member function.  
It is important to understand that these operator functions behave like 
static functions  
when overloaded as member functions despite not being declared with 
static keyword. 
 
static functions can access only the 
static data members that 
are available to the class  
even before an object is created. As we already know that 
new operator is called 
to  
construct objects, it has to be available before the object is constructed. Similarly, 
the  
delete operator is called when the object has already been destructed 
by calling destructor  
of the object.  
Example of Overloading new and delete as Non-members
 
Suppose we want new 
to initialize the contents of a memory block to zero before  
returning it. We can achieve this by writing the operator functions as follows:
 
/* The following program explains the customized new and delete operators */  
#include <iostream.h>  
#include <stdlib.h>  
#include <stddef.h>  
Page 439  
// ------------- Overloaded new operator  
void * operator new ( size_t size )  
{  
void * rtn = calloc( 1, size ); // Calling calloc() to allocate and initialize memory
 
return rtn;  
}  
// ----------- Overloaded delete operator  
void operator delete ( void * ptr )  
{  
free( ptr ); // Calling free() to deallocate memory  
}  
void main()  
{  
// Allocate a zero-filled array  
int *ip = new int[10];  
// Display the array  
for ( int i = 0; i < 10; i ++ )  
cout << " " << ip[i];  
// Release the memory  
delete [] ip;  
}  
The output of the program is as follows.  
0 0 0 0 0 0 0 0 0 0  
Note that the new 
operator takes a parameter of type 
size_t. This parameter 
holds the size  
of the object being allocated, and the compiler automatically sets its value whenever 
we  
use new. Also note 
that the new operator 
returns a void pointer. 
Any new operator 
we  
write must have this parameter and return type.  
In this particular example, 
new calls the standard C function 
calloc to allocate memory
 
and initialize it to zero.  
The delete operator 
takes a void pointer 
as a parameter. This parameter points to the  
block to be deallocated. Also note that the 
delete operator has a
void return type. 
Any  
delete operator we write, must have this parameter and return type.
 
In this example, delete 
simply calls the standard C function 
free to deallocate the
 
memory.  
Example of Overloading new and delete as Members
 
// Class-specific new and delete operators  
Page 440  
#include <iostream.h>  
#include <string.h>  
#include <stddef.h>  
const int MAXNAMES = 100;  
class Name  
{  
public:  
Name( const char *s ) { strncpy( name, s, 25 ); }  
void display() const { cout << '\n' << name; }  
void * operator new ( size_t size );  
void operator delete( void * ptr );  
~Name() {}; // do-nothing destructor  
private:  
char name[25];  
};  
// -------- Simple memory pool to handle fixed number of Names  
char pool[MAXNAMES] [sizeof( Name )];  
int inuse[MAXNAMES];  
// -------- Overloaded new operator for the Name class  
void * Name :: operator new( size_t size )  
{  
for( int p = 0; p < MAXNAMES; p++ )  
if( !inuse[p] )  
{  
inuse[p] = 1;  
return pool + p;  
}  
return 0;  
}  
// --------- Overloaded delete operator for the Names class  
void Name :: operator delete( void *ptr )  
{  
inuse[((char *)ptr - pool[0]) / sizeof( Name )] = 0;  
}  
void main()  
{  
Name * directory[MAXNAMES];  
char name[25];  
for( int i = 0; i < MAXNAMES; i++ )  
{  
Page 441  
cout << "Enter name # " << i+1 << ": ";  
cin >> name;  
directory[i] = new Name( name );  
}  
for( i = 0; i < MAXNAMES; i++ )  
{  
directory[i]->display();  
delete directory[i];  
}  
}  
The output of the above program is given below.  
Enter name # 1: ahmed  
Enter name # 2: ali  
Enter name # 3: jamil  
Enter name # 4: huzaifa  
Enter name # 5: arshad  
Enter name # 6: umar  
Enter name # 7: saleem  
Enter name # 8: kamran  
Enter name # 9: babar  
Enter name # 10: wasim  
ahmed  
ali  
jamil  
huzaifa  
arshad  
umar  
saleem  
kamran  
babar  
wasim  
This program declares a global array called 
pool that can store all 
the Name objects
 
expected. There is also an associated integer array called 
inuse, which contains
true/false  
flags that indicate whether the corresponding entry in the 
pool is in use.  
When the statement directory[i] 
= new Name( name ) is executed, the compiler calls  
the class's new 
operator. The new 
operator finds an unused entry in 
pool, marks it as used,
 
and returns its address. Then the compiler calls 
Name's constructor, which 
uses that  
memory and initializes it with a character string. Finally, a pointer to the resulting 
object  
is assigned to an entry in directory.  
When the statement delete directory[i]
is executed, the compiler calls 
Name 's destructor.  
In this example, the destructor does nothing; it is defined only as a placeholder. 
Then the  
Page 442  
compiler calls the class's 
delete operator. The 
delete operator finds the 
specified object's  
location in the array and marks it as unused, so the space is available for subsequent
 
allocations.  
Note that new is 
called before the constructor, and that 
delete is called after 
the  
destructor.  
Overloading [ ] Operator to Create Arrays
 
We know that if we overload operators 
new and 
delete for a class, those 
overloaded  
operators are called whenever we create an object of that class. However, when we 
create  
an array of those class objects, the global 
operator new( ) is called 
to allocate enough  
storage for the array all at once, and the global 
operator delete( ) is called 
to release that  
storage.  
We can control the allocation of arrays of objects by overloading the special array
 
versions of operator new[ ]
and operator delete[ 
] for the class.  
Previously, while employing global 
new operator to create 
an array of objects, we used to  
tell the delete 
operator by using the array operator( 
[] ) to deallocate memory 
for an  
array. But it is our responsibility to provide or to overload different type of
new and  
different type of delete.
 
There is a common problem when working with arrays. While traversing elements from
 
the array, we might run off the end of the array. This problem might not be caught 
by the  
compiler. However, some latest compilers might be able to detect this. 
 
int iarray [10] ;  
for ( int i = 0; i < 100; i++ )  
{  
// Some code to manipulate array elements  
}  
If a variable is used in the condition of the loop instead of the constant value, 
the  
probability of that error increases. Variable might have an entirely different value 
than  
that anticipated by the programmer.  
We can overcome this problem of array bound by overloading array operator ‘[]’. 
As  
usual before overloading, we should be clear about the functionality or semantics 
of the  
array operator. We use array operator to access an element of array. For example, 
when  
we write iarray[5], 
we are accessing the 6th
element inside array 
iarray. As we want to
 
check for validity of index every time, an array element is accessed. We can do 
this by  
declaring the size of the array using 
#define and checking the
index against the 
size every  
time the array is accessed. 
 
#define MAXNUM 1000  
int iarray [MAXNUM];  
Page 443  
Below is the syntax of declaration line of overloaded array operator: 
 
int& operator [] ( int index ) ;  
In the body of this operator, we can check whether the 
index is greater or equal 
to the  
MAXNUM constant. If this is the case, the function may throw an exception. 
At the  
moment, the function only displays an error message. If 
index is less than
MAXNUM and  
greater than or equal to zero, a reference to the value at the 
index location is returned.
 
Let’s write a class IntArray
and see the array manipulation.  
/*  
The following example defines the IntArray class, where each object contains  
an array of integers. This class overloads the [] operator to perform  
range checking.  
*/  
#include <iostream.h>  
#include <string.h>  
class IntArray  
{  
public:  
IntArray( int len );  
int getLength( ) const;  
int & operator[] ( int index );  
~IntArray( );  
private:  
int length;  
int *aray;  
};  
// ------------ Constructor  
IntArray :: IntArray( int len )  
{  
if( len > 0 )  
{  
length = len;  
aray = new int[len];  
// initialize contents of array to zero  
memset( aray, 0, sizeof( int ) * len );  
}  
else  
{  
length = 0;  
aray = 0;  
}  
Page 444  
}  
// ------------ Function to return length  
inline int IntArray :: getLength() const  
{  
return length;  
}  
// ------------ Overloaded subscript operator  
// Returns a reference  
int & IntArray :: operator []( int index )  
{  
static int dummy = 0;  
if( (index = 0) &&  
(index < length) )  
return aray[index];  
else  
{  
cout << "Error: index out of range.\n";  
return dummy;  
}  
}  
// ------------ Destructor  
IntArray :: ~IntArray()  
{  
delete aray;  
}  
void main()  
{  
IntArray numbers( 10 );  
int i;  
for( i = 0; i < 10; i ++ )  
numbers[i] = i; // Use numbers[i] as lvalue  
for( i = 0; i < 10; i++ )  
cout << numbers[i] << '\n';  
}  
This program first declares 
numbers of type 
IntArray object that can hold ten integers.  
Later, it assigns a value to each element in the array. Note that the array expression
 
appears on the left side of the assignment. This is legal as the 
operator[] function returns
 
a reference to an integer. This means the expression 
numbers[i] acts as an alias 
for an  
element in the private array and it can be the recipient of an assignment statement. 
In this  
situation, returning a reference is not simply more efficient but also necessary.
 
Page 445  
The operator[] function 
checks whether the specified 
index value is within range or not.  
If it is within the range, the function returns a reference to the corresponding 
element in  
the private array. If it is not, the function prints out an error message and returns 
a  
reference to a static 
integer. This prevents out-of-range array references from overwriting  
other regions of memory while causing unexpected program behavior. 
 
Tips
 
• The default constructor
is defined by the C++ compiler automatically for every  
class that has no default constructor (parameterless constructor) defined already.
 
• The default constructor (parameterless constructor) is called for each 
element in  
the array allocated with new.
 
• The new 
operator returns a void 
*, accepts a parameter of type 
size_t. 
 
• The delete 
operator returns nothing (void) 
and accepts a pointer of void 
* to the  
memory block.  
• With new 
operator function, a block of memory is allocated first and then  
constructor is called.  
• With delete 
operator, destructor of the object is called first and then memory  
block is deallocated.  
• By overloading new
and delete 
operators, only allocation and deallocation part  
can be overridden.  
• The same pointer that is returned by the 
new operator, is passed 
as an argument to  
the delete operator. 
These rules apply to both, if operators (new
and delete) 
are  
overloaded as member or non-member operators (as global operators). 
 
• By overloading the array operator ( [] ), one can implement mechanism to 
check  
for array bound.  | 
  
 
 | 
 
  | 
  
 
  | 
 
 
  |