CS 202 Fall 2013  >  Notes for Thursday, September 26, 2013

CS 202 Fall 2013
Notes for Thursday, September 26, 2013

Dynamic Allocation [9.8]

Overview of Memory Allocation

Memory Allocation means obtaining some memory for our program to use. When we are done with it, the memory must be deallocated—returned to the system. In C++ programs, memory allocation can be done in three ways.

Automatic Allocation
This is the way normal local variables are handled. Roughly speaking, the memory is allocated when the opening brace “{” is encountered and deallocated at the closing brace “}”.
Static Allocation
This is the way global variables, static local variables, and static member variables are handled. Memory is allocated when a program is loaded (before it executes). It is deallocated when the program ends.
Dynamic Allocation
Here our program has control over when memory is allocated and deallocated. A new statement requests memory; a delete statement deallocates it.

Like automatic and static allocation, dynamic allocation does not give us raw memory; we specify a type, and the memory is initialized by calling a constructor. When we are done with it, the destructor is called.

Unlike automatic and static allocation, the values we obtain using dynamic allocation have no name. Instead of placing our value in some named location, we get a pointer to the value, which we must store somewhere if we are to use the allocated memory.

In C++, dynamic allocation is the primary way to obtain an arbitrarily sized block of memory while a program is running. If someone says, “But vector does that,” then I reply, “Yes! But how does vector do it?” The answer is that the code that implements vector does dynamic allocation, using new and delete. As knowledgeable programmers, we should know not only how to use vector, but also how to write it.

In addition, we will need dynamic allocation in order to do some aspects of object-oriented programming.

Non-Array Form

To dynamically allocate a value of type Foo, we do

[C++]

new Foo

This allocates enough memory for a value of type Foo, calls the Foo default constructor, and returns a pointer to the value. We need to store this pointer.

[C++]

Foo * fp = new Foo;
When we are done, we do a delete on the pointer.

[C++]

delete fp;

This calls the Foo destructor and deallocates the memory that the pointer points to. It does not do anything to the pointer. So when the delete is done, the pointer still points to the same location in memory, but this is no longer a location we are allowed to use. Do not use it!

It is very important that each dynamically allocated object be deallocated.

For every new there should be exactly one delete!

Between the new and the delete we can use the value.

[C++]

int * ip = new int;
*ip = 3;
*ip += 5;
cout < < (*ip) << endl;  // Should print 8
delete ip;

Array Form

We can also use new to allocate an array of multiple values. We do this by putting brackets after the type. The values obtained in this way are always default-constructed.

[C++]

Foo * fa = new Foo[k];

If we allocated using the array form, then we also need to deallocate using the array form: put a pair of empty brackets “[]” just after the delete.

[C++]

delete [] fa;

Between the new and the delete we can treat the pointer as if it is an array (roughly, a vector without the member functions).

[C++]

int * ia = new int[7];
for (int c = 0; c <7; ++c)
{
    ia[c] = c*c;
}
for (int c = 0; c < 7; ++c)
{
    cout << c[i] << endl;
}
delete [] ia;

RAII

Dynamic allocation without the corresponding deallocation has been a thorn in the side of C and C++ programmers for decades. The vector class handles this nicely; I still recommend it.

But dynamic allocation does have its place, as we will see. When you use it, one way to make sure that the delete happens is to have the dynamic values managed by an object. Perhaps the new can be done in the object’s constructor (or perhaps not). Then the destructor can do the delete. If the managing object is automatic or static, then the destructor call is guaranteed, and so the delete is guaranteed to happen as well.

This is a common C++-programming idiom. It is universally referred to as “Resource Acquisition Is Initialization”—an unfortunate name, I think. But the abbreviation “RAII” is not so bad.

Summary

  Non-Array Array
Allocate Foo * fp = new Foo;
OR
Foo * fp = new Foo(1, "abc");
Foo * fa = new Foo[k];
Use *fp = x;
cout << *fp << endl;
fp->bar();
fa[i] = x;
cout << fa[i] << endl;
fa[i].bar();
Deallocate
In a destructor is a
good place for this.
delete fp; delete [] fa;

Classes II: The Copy Constructor [14.4]

When an object is passed by value to a function, a copy is made. The constructor called in such a situation is the copy constructor.

[C++]

void bar(Foo f)
{
    ...
private:
    int _a;
    string _b;
};

Foo x;
bar(x);  // f is a copy of x, constructed with the Foo copy ctor

A class’s copy constructor is just a constructor that takes a single parameter whose type is the class. We cannot pass this by value (think about it). The standard way to do this is to pass by reference-to-const. This is just like passing by reference, except that we put a “const” before the parameter type; this ensures that the value cannot be modified.

[C++]

class Foo {
public:

    // Copy ctor
    Foo (const Foo & other)
    {
        ...
    }
    ...

We normally do not need to write a copy constructor. If we do not declare it, then the compiler will write one for us. The compiler-generated copy constructor will construct all data members using their copy constructors, passing as parameters the corresponding data members of the other object.

When do we write a copy constructor? A good rule of thumb is to write it when you write a destructor. So, if an object manages some dynamically allocated memory, and you need to give the class a destructor so that it can do a delete, then you probably need to write a copy constructor, too.

See hasarray.h and hasarray.cpp for the code for class HasArray, which, among other things, does dynamic allocation and has a copy constructor.

See hasarray_main.cpp for a C++ program that uses class HasArray.

For today’s lab work, see the 9/26 Challenge.


CS 202 Fall 2013: Notes for Thursday, September 26, 2013 / Updated: 26 Sep 2013 / Glenn G. Chappell