5.11. The new and delete ExpressionsIn Section 4.3.1 (p. 134) we saw how to use new and delete expressions to dynamically allocate and free arrays. We can also use new and delete to dynamically allocate and free single objects. When we define a variable, we specify a type and a name. When we dynamically allocate an object, we specify a type but do not name the object. Instead, the new expression returns a pointer to the newly allocated object; we use that pointer to access the object: int i; // named, uninitialized int variable int *pi = new int; // pi points to dynamically allocated, // unnamed, uninitialized int This new expression allocates one object of type int from the free store and returns the address of that object. We use that address to initialize the pointer pi. Initializing Dynamically Allocated ObjectsDynamically allocated objects may be initialized, in much the same way as we initialize variables: int i(1024); // value of i is 1024 int *pi = new int(1024); // object to which pi points is 1024 string s(10, '9'); // value of s is "9999999999" string *ps = new string(10, '9'); // *ps is "9999999999" We must use the direct-initialization syntax (Section 2.3.3, p. 48) to initialize dynamically allocated objects. When an initializer is present, the new expression allocates the required memory and initializes that memory using the given initializer(s). In the case of pi, the newly allocated object is initialized to 1024. The object pointed to by ps is initialized to a string of 10 nines. Default Initialization of Dynamically Allocated ObjectsIf we do not explicitly state an initializer, then a dynamically allocated object is initialized in the same way as is a variable that is defined inside a function. (Section 2.3.4, p. 50) If the object is of class type, it is initialized using the default constructor for the type; if it is of built-in type, it is uninitialized. string *ps = new string; // initialized to empty string int *pi = new int; // pi points to an uninitialized int As usual, it is undefined to use the value associated with an uninitialized object in any way other than to assign a good value to it.
We can also value-initialize (Section 3.3.1, p. 92) a dynamically allocated object: string *ps = new string(); // initialized to empty string int *pi = new int(); // pi points to an int value-initialized to 0 cls *pc = new cls(); // pc points to a value-initialized object of type cls We indicate that we want to value-initialize the newly allocated object by following the type name by a pair of empty parentheses. The empty parentheses signal that we want initialization but are not supplying a specific initial value. In the case of class types (such as string) that define their own constructors, requesting value-initialization is of no consequence: The object is initialized by running the default constructor whether we leave it apparently uninitialized or ask for value-initialization. In the case of built-in types or types that do not define any constructors, the difference is significant: int *pi = new int; // pi points to an uninitialized int int *pi = new int(); // pi points to an int value-initialized to 0 In the first case, the int is uninitialized; in the second case, the int is initialized to zero.
int x(); // does not value initialize x declares a function named x with no arguments that returns an int. Memory ExhaustionAlthough modern machines tend to have huge memory capacity, it is always possible that the free store will be exhausted. If the program uses all of available memory, then it is possible for a new expression to fail. If the new expression cannot acquire the requested memory, it throws an exception named bad_alloc. We'll look at how exceptions are thrown in Section 6.13 (p. 215). Destroying Dynamically Allocated ObjectsWhen our use of the object is complete, we must explicitly return the object's memory to the free store. We do so by applying the delete expression to a pointer that addresses the object we want to release. delete pi; frees the memory associated with the int object addressed by pi.
The effect of deleting a pointer that addresses memory that was not allocated by new is undefined. The following are examples of safe and unsafe delete expressions: int i; int *pi = &i; string str = "dwarves"; double *pd = new double(33); delete str; // error: str is not a dynamic object delete pi; // error: pi refers to a local delete pd; // ok It is worth noting that the compiler might refuse to compile the delete of str. The compiler knows that str is not a pointer and so can detect this error at compile-time. The second error is more insidious: In general, compilers cannot tell what kind of object a pointer addresses. Most compilers will accept this code, even though it is in error. delete of a Zero-Valued PointerIt is legal to delete a pointer whose value is zero; doing so has no effect:
int *ip = 0;
delete ip; // ok: always ok to delete a pointer that is equal to 0
The language guarantees that deleting a pointer that is equal to zero is safe. Resetting the Value of a Pointer after a deleteWhen we write delete p; p becomes undefined. Although p is undefined, on many machines, p still contains the address of the object to which it pointed. However, the memory to which p points was freed, so p is no longer valid. After deleting a pointer, the pointer becomes what is referred to as a dangling pointer. A dangling pointer is one that refers to memory that once held an object but does so no longer. A dangling pointer can be the source of program errors that are difficult to detect.
Dynamic Allocation and Deallocation of const ObjectsIt is legal to dynamically create const objects: // allocate and initialize a const object const int *pci = new const int(1024); Like any const, a dynamically created const must be initialized when it is created and once initialized cannot be changed. The value returned from this new expression is a pointer to const int. Like the address of any other const object, the return from a new that allocates a const object may only be assigned to a pointer to const. A const dynamic object of a class type that defines a default constructor may be initialized implicitly: // allocate default initialized const empty string const string *pcs = new const string; This new expression does not explicitly initialize the object pointed to by pcs. Instead, the object to which pcs points is implicitly initialized to the empty string. Objects of built-in type or of a class type that does not provide a default constructor must be explicitly initialized.
Deleting a const ObjectAlthough the value of a const object cannot be modified, the object itself can be destroyed. As with any other dynamic object, a const dynamic object is freed by deleting a pointer that points to it: delete pci; // ok: deletes a const object Even though the operand of the delete expression is a pointer to const int, the delete expression is valid and causes the memory to which pci refers to be deallocated. |