Section 5.11. The new and delete Expressions


5.11. The new and delete Expressions

In 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 Objects

Dynamically 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 Objects

If 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.

Just as we (almost) always initialize the objects we define as variables, it is (almost) always a good idea to initialize dynamically allocated objects.



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.

The () syntax for value initialization must follow a type name, not a variable. As we'll see in Section 7.4 (p. 251)


      int x(); // does not value initialize x 

declares a function named x with no arguments that returns an int.


Memory Exhaustion

Although 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 Objects

When 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.

It is illegal to apply delete to a pointer that addresses memory that was not allocated by new.



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 Pointer

It 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 delete

When 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.

Setting the pointer to 0 after the object it refers to has been deleted makes it clear that the pointer points to no object.



Dynamic Allocation and Deallocation of const Objects

It 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.

Caution: Managing Dynamic Memory Is Error-Prone

The following three common program errors are associated with dynamic memory allocation:

  1. Failing to delete a pointer to dynamically allocated memory, thus preventing the memory from being returned to the free store. Failure to delete dynamically allocated memory is spoken of as a "memory leak." Testing for memory leaks is difficult because they often do not appear until the application is run for a test period long enough to actually exhaust memory.

  2. Reading or writing to the object after it has been deleted. This error can sometimes be detected by setting the pointer to 0 after deleting the object to which the pointer had pointed.

  3. Applying a delete expression to the same memory location twice. This error can happen when two pointers address the same dynamically allocated object. If delete is applied to one of the pointers, then the object's memory is returned to the free store. If we subsequently delete the second pointer, then the free store may be corrupted.

These kinds of errors in manipulating dynamically allocated memory are considerably easier to make than they are to track down and fix.


Deleting a const Object

Although 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.

Exercises Section 5.11

Exercise 5.30:

Which of the following, if any, are illegal or in error?

      (a) vector<string> svec(10);      (b) vector<string> *pvec1 = new vector<string>(10);      (c) vector<string> **pvec2 = new vector<string>[10];      (d) vector<string> *pv1 = &svec;      (e) vector<string> *pv2 = pvec1;      (f) delete svec;      (g) delete pvec1;      (h) delete [] pvec2;      (i) delete pv1;      (j) delete pv2; 




C++ Primer
C Primer Plus (5th Edition)
ISBN: 0672326965
EAN: 2147483647
Year: 2006
Pages: 223
Authors: Stephen Prata

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net