Dynamic Memory Allocation

Allocate an array with an unpredictable number of elements

When you create an array in C++, you must always specify the number of elements in the array when you write the program. Here are two ways to do that. (Use the data type size_t for a variable that holds the number of elements in an array, or the number of bytes in a block of memory.)

	int a[3];   //Create an array containing 3 elements.

	int b[] {   //Create another array containing 3 elements.
		10,
		20,
		30
	};

	//If you need a variable holding the number of elements in the array b,
	const size_t n {size(b)};

If you don’t know the number of elements when you write the program, then you can’t make an array. For example, you can’t get the number of elements from input after the program has started running:

	cout << "How many elements do you need in your array? ";
	size_t n {0};
	cin >> n;
	int a[n];   //Try to create an array with n elements.  Won't compile.

The simplest application of dynamic memory allocation (written with the operators new and delete) is to create an array when you can’t predict when you’re writing the program how many array elements you will need.

  1. new1.C. Loop through the block of memory with a size_t i subscript.
  2. new2.C. Loop through the block of memory with an int *q pointer.
    What happens if the user requests too much dynamically allocated memory?
    How many ints do you want to store? 99999999999
    terminate called after throwing an instance of 'std::bad_alloc'
      what():  std::bad_alloc
    Aborted (core dumped)
    
    echo $?     (See the exit status number producted by the program.)
    134         (134 = 128 + 6.  6 is number of the abort signal SIGABRT.)
    
  3. new3.C. Catch the bad_alloc exception.
    How many ints do you want to store? 99999999999
    Sorry, Linux has no room for 99999999999 ints.        (Civilized error message.)
    
    echo $?                                               (Civilized exit status.)
    1
    
  4. vector.C. Easier to use a standard library vector object instead of new and delete.
    v is our first example of an object.
    The name of the data type of this object is std::vector<int>. (A data type that has a pair of <angle brackets> in its name is a template data type.)
    I’m demonstrating two of the member functions of v, push_back and size.
    Exercise.
    Insert the follwong statements into vector.C immediately after the call to push_back.
    		cout << "v.size() = " << v.size() << "\n";
    		cout << "v.capacity() = " << v.capacity() << "\n";
    
    Does the capacity grow faster than the size? Does the capacity ever suddenly double as the vector increases in size?

Allocate an unpredictable number of variables

… and deallocate them in an unpredictable order.

  1. single1.C. Let the user type in an unpredictable number of integers, and hold them in a singly linked list, placing each new integer at the head of the list. The user doesn’t have to know in advance how many integers there will be. Each integer is stored in a separate island—a structure that also contains a pointers to the next island, if any. The pointer head points to the first island in the list, if any.
    I wish I could have written the final loop like this, but why would it have failed?
            //Deallocate all the nodes.
            for (node *p {head}; p != nullptr; p = p->next) {
                    delete p;               //No square brackets to delete a scalar.
            }
    
  2. single2.C. Just like single1.C, but places each new integer at the end of the list. This allows the output to be in the same order as the input. single2.C has a tail pointer as well as a head pointer.
  3. double.C. Let the user type in an unpredictable number of integers, and hold them in a doubly linked list in increasing size order. The user doesn’t have to know in advance how many integers there will be. Each integer is stored in a separate island—a structure that also contains pointers to the previous and next island, if any. The pointer head points to the first island in the list, if any.
  4. list.C. To hold unpredictably many ints in order of increasing size, it’s easier to use a standard library list object instead of new and delete.