








Study with the several resources on Docsity
Earn points by helping other students or get them with a premium plan
Prepare for your exams
Study with the several resources on Docsity
Earn points to download
Earn points by helping other students or get them with a premium plan
The concepts of arrays of objects and overloading new and delete operators in c++. It covers the declaration and initialization of arrays of objects, dynamic allocation and deallocation of arrays of objects, and the importance of using the correct delete operator when dealing with arrays. The document also explains how to overload new and delete operators as members or non-members and provides examples of their usage.
Typology: Study notes
1 / 14
This page cannot be seen from the preview
Don't miss anything!









a
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
A class is a user-defined data type. Objects are instances of classes the way int variables are instances of int s. Previously, we have worked with arrays of int s. 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. / Date( ) { cout << "\n Parameterless constructor called ..."; month = day = year = 0; } / Parameterized constructor; has three int s 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:
Date yourDate [3] = { Date(10, 24, 1980), Date(06, 14, 1985), Date(07, 09,1986) };
docsity.com
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:
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 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
docsity.com
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.
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
docsity.com
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>
// ------------- 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 } main()
docsity.com
// 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
#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]; };
docsity.com
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 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.
docsity.com
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 6 th^ 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];
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;
docsity.com
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. 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).
docsity.com
By overloading the array operator ( [] ), one can implement mechanism to check for array bound.
docsity.com