ikerhurtado.com
You're in
Iker Hurtado's pro blog
Developer | Entrepreneur | Investor
Software engineer (entrepreneur and investor at times). These days doing performant frontend and graphics on the web platform at Barcelona Supercomputing Center

Relearning C++: Notes on references, pointers and dynamic memory management

5 Mar 2015   |   iker hurtado  
Share on Twitter Share on Google+ Share on Facebook
In this post I write down some notes as from my study of C++ on references, pointers and dynamic memory management

References

A reference defines an alternative name for an object. A reference is not an object but just another name for an already existing object.

It's necessary to initialize a reference during declaration. Once initialized, it remains bound to its initial object.

Reference is closely related to pointer. In many cases, it can be used as an alternative to pointer but referencing and dereferencing are done on the references implicitly.

References are typically used for one of two purposes: they are often used as function parameters to pass objects by reference (avoiding the use of pointers) and another primary use is to provide easier access to nested data:

int &rValue = outerClassInstance.innerClassInstance.value;
rValue can be used in place of outerClassInstance.innerClassInstance.value.

Enlace al uso en funciones si es el caso

Pointers

A pointer is a compound type that points to another type; it's used for indirect access to other objects. A pointer variable is an ordinary object that stores a memory address and, therefore, can be assigned and copied. A single pointer can point to several different objects over its lifetime.

Dereferencing a pointer (*p) yields the object to which the pointer points. So, it can be used as lvalue or rvalue in expressions.

When a pointer variable is declared, its content is not initialized. It contains an invalid address. Hence, it's advisable to initialize pointers using the literal nullptr -has a special type that can be converted to any other pointer type.

A void pointer (void *) can hold address of any data type (except function pointer); it's very flexible. The trade-off is that the data pointed by it cannot be directly dereferenced, and for that reason, any address in a void pointer needs to be transformed.

This type of pointers can be compared (their addresses) and used to pass generic parameters to a function.

Dynamic memory management

Often some memory needs of a program can only be determined at runtime, then we use dynamically memory allocation. This feature allows us to allocate memory of whatever size we want when we need it. Dynamic allocated entities are handled through pointers.

The programmer has the responsibility to handle the memory allocation and deallocation (via new and delete operators) and has full control on the pointer addresses and their contents, as well as memory management.

The new operation returns a pointer to the memory allocated. The delete operator takes a pointer (pointing to the memory allocated via new) as its sole argument. Examples with a fundamental type var and a object:

// using an initializer to initialize a fundamental type
int * p = new int(8); / int * p = new int {8}; // C++11 brace initialization syntax
 
// invoking a constructor to initialize an object (of a class)
Date * date1 = new Date(2015, 2, 1);  

Allocating blocks (arrays) of objects

If we need a sequence of objects (with the same type) we can allocate them contiguously in a memory block. We'll use the array form of new (new[]) and delete (delete[]). Its syntax is:

int *p = new int[nSize];
delete[] p;
new returns a pointer to the beginning of the new block of memory allocated.

The elements of the block can be accessed, like arrays, either with p[n] or *(n+1) forms.

Memory leaks

Dynamically allocated memory stays allocated until that is explicitly deallocated or the program ends. However, the pointers used to access dynamically allocated memory follow the scoping rules of normal variables. This can be source of memory leaks.

A memory leak is a piece of dynamically allocated memory that has not been deleted after using. Memory leaks eat up free memory while the program is running, making less memory available.

It's a good practice to assign pointers to nullptr (0), both when they are declared (unless assigned to another address), and after they are deleted.

Checking the allocation result

The dynamic memory requested by a program is allocated by the system to the memory heap. As said, the computer memory is a limited resource and there are no guarantees that all requests to allocate memory are going to be fulfilled by the system.

C++ provides two standard mechanisms to check if the allocation was successful: The default method is by handling exceptions. An exception of type bad_alloc is thrown when the allocation fails; if it is not handled by a specific handler, the program execution is terminated.

The other method is known as nothrow. In this case, when a memory allocation fails the pointer returned by new is a null pointer, and the program continues its execution normally. This method can be specified like this:

p = new (nothrow) int [5]; 

This method likely bring to produce less efficient code than exceptions, since it implies explicitly checking the pointer value returned.


The operators new and delete are not available in the C language; instead, it used a library solution, with the functions malloc, calloc, realloc and free, defined in the header (known as in C). The functions are also available in C++ and can also be used to allocate and deallocate dynamic memory.

Smart pointers

Three interesting resources I have read about this topic:

POST A COMMENT: