Dynamic Memory Management with Operators new and delete
C++ enables programmers to control the allocation and deallocation of memory in a program for any built-in or user-defined type. This is known as dynamic memory management and is performed with operators new and delete. Recall that class Employee (Figs. 10.1210.13) uses two 25-character arrays to represent the first and last name of an Employee. The Employee class definition (Fig. 10.12) must specify the number of elements in each of these arrays when it declares them as data members, because the size of the data members dictates the amount of memory required to store an Employee object. As we discussed earlier, these arrays may waste space for names shorter than 24 characters. Also, names longer than 24 characters must be truncated to fit in these fixed-size arrays.
Wouldn't it be nice if we could use arrays containing exactly the number of elements needed to store an Employee's first and last name? Dynamic memory management allows us to do exactly that. As you will see in the example of Section 10.7, if we replace array data members firstName and lastName with pointers to char, we can use the new operator to dynamically allocate (i.e., reserve) the exact amount of memory required to hold each name at execution time. Dynamically allocating memory in this fashion causes an array (or any other built-in or user-defined type) to be created in the free store (sometimes called the heap)a region of memory assigned to each program for storing objects created at execution time. Once the memory for an array is allocated in the free store, we can gain access to it by aiming a pointer at the first element of the array. When we no longer need the array, we can return the memory to the free store by using the delete operator to deallocate (i.e., release) the memory, which can then be reused by future new operations.
Again, we present the modified Employee class as described here in the example of Section 10.7. First, we present the details of using the new and delete operators to dynamically allocate memory to store objects, fundamental types and arrays.
Consider the following declaration and statement:
Time *timePtr; timePtr = new Time;
The new operator allocates storage of the proper size for an object of type Time, calls the default constructor to initialize the object and returns a pointer of the type specified to the right of the new operator (i.e., a Time *). Note that new can be used to dynamically allocate any fundamental type (such as int or double) or class type. If new is unable to find sufficient space in memory for the object, it indicates that an error occurred by "throwing an exception." Chapter 16, Exception Handling, discusses how to deal with new failures in the context of the ANSI/ISO C++ standard. In particular, we will show how to "catch" the exception thrown by new and deal with it. When a program does not "catch" an exception, the program terminates immediately. [Note: The new operator returns a 0 pointer in versions of C++ prior to the ANSI/ISO standard. We use the standard version of operator new throughout this book.]
To destroy a dynamically allocated object and free the space for the object, use the delete operator as follows:
delete timePtr;
This statement first calls the destructor for the object to which timePtr points, then deallocates the memory associated with the object. After the preceding statement, the memory can be reused by the system to allocate other objects.
Common Programming Error 10.8
Not releasing dynamically allocated memory when it is no longer needed can cause the system to run out of memory prematurely. This is sometimes called a "memory leak." |
C++ allows you to provide an initializer for a newly created fundamental-type variable, as in
double *ptr = new double( 3.14159 );
which initializes a newly created double to 3.14159 and assigns the resulting pointer to ptr. The same syntax can be used to specify a comma-separated list of arguments to the constructor of an object. For example,
Time *timePtr = new Time( 12, 45, 0 );
initializes a newly created Time object to 12:45 PM and assigns the resulting pointer to timePtr.
As discussed earlier, the new operator can be used to allocate arrays dynamically. For example, a 10-element integer array can be allocated and assigned to gradesArray as follows:
int *gradesArray = new int[ 10 ];
which declares pointer gradesArray and assigns it a pointer to the first element of a dynamically allocated 10-element array of integers. Recall that the size of an array created at compile time must be specified using a constant integral expression. However, the size of a dynamically allocated array can be specified using any integral expression that can be evaluated at execution time. Also note that, when allocating an array of objects dynamically, the programmer cannot pass arguments to each object's constructor. Instead, each object in the array is initialized by its default constructor. To delete the dynamically allocated array to which gradesArray points, use the statement
delete [] gradesArray;
The preceding statement deallocates the array to which gradesArray points. If the pointer in the preceding statement points to an array of objects, the statement first calls the destructor for every object in the array, then deallocates the memory. If the preceding statement did not include the square brackets ([]) and gradesArray pointed to an array of objects, only the first object in the array would receive a destructor call.
Common Programming Error 10.9
Using delete instead of delete [] for arrays of objects can lead to runtime logic errors. To ensure that every object in the array receives a destructor call, always delete memory allocated as an array with operator delete []. Similarly, always delete memory allocated as an individual element with operator delete. |