Login

Username:

Password: 

Join us Now |  Forgot Password? | Forgot UserName?

C++

Learn step by step



Dynamic memory

A good understanding of how dynamic memory really works in C++ is essential to becoming a good C++ programmer. Memory in your C++ program is divided into two parts:

  • Stack: All variables declared inside function will take up memory from the stack.
  • Heap: This is unused memory of program and can be used to allocate the memory dynamically when program runs.

Many times, we are not aware in advance how much memory we will need to store particular information in a defined variable and the size of required memory can be determined at run time.

We can allocate memory at run time within the heap for the variable of a given type using a special operator in C++ which returns the address of the space allocated. This is called new operator.

If we are not in need of dynamically allocated memory anymore, we can use delete operator, which de-allocates memory previously allocated by new operator.

Operators new and new[]

Dynamic memory is allocated using operator called new. new operator is followed by a data type specifier and, if sequence of more than one element is required, the number of these within brackets [ ]. It returns a pointer to the beginning of the new block of memory allocated.
Its syntax is:

pointer = new type
pointer = new type [number_of_elements]

The first expression is used to allocate memory to contain one single element of type type. And second one is used to allocate a block (an array) of elements of specified type, where number_of_elements is integer value representing the amount of these.
For example:

int * jay;
jay = new int [5];

In this case, the system dynamically allocates space for five elements of type int and returns a pointer to the first element of the sequence, which is assigned to jay (a pointer). Therefore, jay now points to a valid block of memory with space for five elements of type int.

Fig 1.

Here, jay is a pointer, and thus, the first element pointed to by jay can be accessed either with the expression jay[0] or the expression *jay (both are equivalent). Second element can be accessed either with jay[1] or *(jay+1), and so on...

There is a difference between declaring a normal array and allocating dynamic memory for a block of memory using new. Most important difference is that size of a regular array needs to be a constant expression, and thereby its size has to be determined at the moment of designing of program, before it is run, whereas the dynamic memory allocation performed by new allows us to assign memory during runtime using any variable value as size.

C++ supports operator overloading, & one of the operators it lets us overload is new. Example:


class Base
{
public:
void* operator new(size_t sz)
{
cerr << "new " << sz << " bytes\n";
return ::operator new(sz);
}

void operator delete(void* p)
{
cerr << "delete\n";
::operator delete(p);
}
private:
int m_data;
};

class Derived : public Base
{
private:
int m_derived_data;
vector<int> z, y, x, w;
};

int main()
{
Base* b = new Base;
delete b;

Derived* d = new Derived;
delete d;
return 0;
}

Output

new 4 bytes
delete
new 56 bytes
delete
C++ provides two standard mechanisms to check if the allocation was successful:

  • One is by handling exceptions. Using this, an exception of type bad_alloc is thrown when allocation fails. Exceptions are a powerful C++ feature explained later. But for now, we should know that if this exception is thrown and it is not handled by a specific handler, then the execution of program is terminated.

    This is the exception method that is used by default by new, and is the one that is used in a declaration like:
    jay = new int [5];          // if allocation fails, a exception is thrown
  • Second method is nothrow, and in this when memory allocation fails, instead of throwing a bad_alloc exception or terminating the program, the pointer returned by new is null pointer, and the program continues its execution normally.

    This method can be specified by using a special object nothrow, declared in header , as argument for new:
    jay = new (nothrow) int [5];

In that case, if allocation of this block of memory fails, failure can be detected by checking if jay is a null pointer:

jint * jay;
jay = new (nothrow) int [5];
if (jay == nullptr) {
        // error assigning the memory. Take measures.
}

This nothrow is likely to produce less efficient code than exceptions, because it implies explicitly checking the pointer value returned after each and every allocation. So, the exception mechanism is generally preferred over it, at least for critical allocations. Still, most of upcoming examples will use the nothrow mechanism due to its simplicity.

Operators delete and delete[ ]

In most cases, the memory allocated dynamically is only needed during a specific time periods within a program; once it is no longer needed, then it can be freed so that the memory becomes available again for other requests of dynamic memory.
This is the purpose of operator delete:

delete pointer;
delete[] pointer;

First statement releases memory of single element allocated using new operator, and the second one releases the memory allocated for arrays of elements using new and a size in brackets([ ]).

Value passed as a argument to delete shall be either a pointer to a memory block previously allocated with new, or it shall be a null pointer (in the case of a null pointer, delete produces no effect).


        // rememb-o-matic
#include <iostream>
#include <new>
using namespace std;

int main ()
{
int i,n;
int * p;
cout << "How many numbers would you like to type? ";
cin >> i;
p= new (nothrow) int[i];
if (p == nullptr)
cout << "Error: memory could not be allocated";
else
{
for (n=0; n<i; n++)
{
cout << "Enter number: ";
cin >> p[n];
}
cout << "You have entered: ";
for (n=0; n<i; n++)
cout << p[n] << ", ";
delete[] p;
}
return 0;
}

Output

How many numbers would you like to type? 5
Enter number : 75
Enter number : 436
Enter number : 1067
Enter number : 8
Enter number : 32
You have entered: 75, 436, 1067, 8, 32,

Notice how the value within brackets in the new statement is a variable value entered by the user (i), not a constant expression:

p= new (nothrow) int[i];

There always exists a possibility that the user introduces a value for i so big that the system cannot allocate enough memory for it.

It is considered to be a good practice for programs to always able to handle failures to allocate memory, either by checking the pointer value(if nothrow) or catching the proper exception.



Related Videos