Section 7 - Dynamic Arrays

2A.7.1 Arrays From Pointers

Think back a short distance.  We introduced pointers along side simple variables and allocated them in pairs:

float x, *fp;
fp = &x;

We could use either the x or the dereferenced *fp to access the same float object.  Very shortly after that, I said, "forget the float x, just declare the pointer fp"  and we used the new operator to create the float dynamically:

float *fp = new float;

Well, we are at a similar crossroad.  Last section we declared a bracketed array alongside a pointer and used the pointer as an alternative to the array name to access the elements of the array. Compare this to two boxes up:

float dogs[100], *dogPtr;
dogPtr = dogs;

We now want to lose the dog[] array entirely.  We wish to only declare the pointer and then, whenever we feel like it, dynamically allocate a block of memory for dogPtr to point to.  If we can do that, we really start to see the usefulness of dynamic memory allocation.  Have a look:

double *temperature;
long numCities;

cout << "How many cities would you like to track? ";
cin >> numCities;

temperature = new double[numCities];

Voila! We have an array of doubles, exactly the right size.

If the user enters 100 in response to the query, we will have 100 doubles allocated for this dynamic temperature array. This is nothing more than the dynamic allocation we saw earlier except that before we were allocating only one variable at a time:

double *dp;
dp = new double;

Now, however, we are allocating a large block of variables in a single new statement:

double *temperature;
temperature = new double[1000];

As with dynamic allocation of single variables, allocating a block of variables requires that we eventually delete the memory.  We do this with delete:

delete[] temperature;

at some point before the entire program terminates.   We use brackets here because we want to delete the entire block that temperature points to, not just one object.  When you have created a dynamic array like this, use brackets with delete to free up the entire array.

Here is one example of this.

int main()
{
  double *temperature;
  long numCities, k;

  cout << "How many cities would you like to track? ";
  cin >> numCities;
  temperature = new double[numCities];

  for (k = 0; k < numCities; k++)
  {
    cout << "High in city #" << k << "? ";
    cin >> temperature[k];
  }

  for (k=0; k < numCities; k++)
    cout << "High in city #" << k << " is "
  << temperature[k] << endl;

  // do something useful with the temperatures (omitted here)

  delete [] temperature;
  temperature = NULL; // not required but good after deleting

  cout << "Now how many cities would you like to track? ";
  cin >> numCities;
  temperature = new double[numCities];

  // program goes on and processes this
  // new array ...
  // ...
   
  return 0;
}

With output:

/* ------------------- SAMPLE RUN ---------------
How many cities would you like to track? 7
High in city #0? 56
High in city #1? 35
High in city #2? 87
High in city #3? 23
High in city #4? 56
High in city #5? 87
High in city #6? 23
High in city #0 is 56
High in city #1 is 35
High in city #2 is 87
High in city #3 is 23
High in city #4 is 56
High in city #5 is 87
High in city #6 is 23
Now how many cities would you like to track? 90

... etc.

The point is that we can release the original array and resize it to fit the new data, mid-program.  This is what puts the dynamic in dynamic memory allocation.

2A.7.2 Dynamic Array of Cards

From what we learned, we can now create a dynamic array of cards:

Card *deck;
deck = new Card[52];
This is a stripped down version of the Deck class -- without the class.

Then we can manually set individual elements:

deck[15].set('3', diamonds);

But this would be kind of silly since we are programmers and would never think of manually setting all 52 cards.  One assignment I might have given you was to come up with a main() program that set the 52 Card values as efficiently as possible. I decided it was too much fun and I wanted to do it myself.  So here it is.  You can see the tricks and techniques I used.  There is nothing fancy going on here -- except for the dynamic allocation of the array, this is something you might have done in a previous C++ course.

See if you can understand how it works.  Can you find a more efficient algorithm for setting the cards?

Card *deck;
Card::Suit st;
int k, j;
char val;

deck = new Card[52];

for (k = 0; k < 4; k++)
{
  // set the suit for this loop pass
  st = static_cast<Card::Suit>(k);  // or old style:  st = (Card::Suit)k;

  // now set all the values for this suit
  deck[13*k].set('A', st);
  for (val='2', j = 1; val<='9'; val++, j++)
  deck[13*k + j].set(val, st);
  deck[13*k+9].set('T', st);
  deck[13*k+10].set('J', st);
  deck[13*k+11].set('Q', st);
  deck[13*k+12].set('K', st);
}

for (k = 0; k < 52; k++)
  cout << deck[k].toString() << endl;
cout << endl;

delete[] deck;
console shot

Here are some very interesting questions for you to ponder: