3.1. Freeing an Object from Memory Explicitly#

In the previous chapter, we discuss that when an object is created, the constructor is called, and when it goes out of scope, the destructor is called automatically. However, in another case, if the object was dynamically allocated, the destructor is explicitly called when an object is freed. We need to recap dynamic memory allocation before we look at an example of this case.

3.1.1. Recap: Dynamic Memory Allocation#

What happens when a program runs? A computer allocates memory in Random Access Memory (RAM) (or another named is main memory) for the program’s variables and objects. There are four segments in this memory:

  • Stack: Stores the local variables and objects of a function

  • Heap: Stores the dynamically allocated memory,

  • Globals + Const variables: Stores the constants and global variables of your program

  • Code: Stores the instructions of the program

In the following example, we will see the stack used to store local variables and the heap storing dynamically allocated memory.

3.1.1.1. Step 1: Stack Only#

In Fig. 3.1, in the first two lines, we declare and initialize x to 10 and declare and initialize pointer p of type int* to NULL. Both x and p are stored in the stack as they are local variables.

In p = &x, we use the reference operator & before x to get the address of x and we store it in p. Using p, we can now gain access to x.

In *p = 5, we use the deference operator * before p to dereference it, which means we access the value stored at the address that p points to. We can say “we dereference the reference to x,” which is x. This changes the line *p = 5 \(\rightarrow\) *(&x) = 5 \(\rightarrow\) x = 5, allowing allows us to change the value of x to 5.

Code in main.cpp

Step 1

Fig. 3.1 The stack stores local variables: int x and int* p, where p stores the address of x in the stack, and then through p we change the value of x from 10 to 5.#

3.1.1.2. Step 2: Dynamic Memory Allocation#

In Fig. 3.2, we dynamically allocate memory for an integer using the new operator. The new operator allocates memory on the heap, and it returns the address to that memory. Hence, in p = new int, p now points to the heap memory where the integer is stored. We can access the value stored in that memory using *p.

In *p = 20, we again dereference p to change the value stored in the heap memory to 20.

Code in main.cpp

Step 2

Fig. 3.2 Dynamically allocating memory using new operator. The pointer p now points to the heap memory where the integer is stored, and we can access it using *p.#

3.1.1.3. Step 3: Freeing Memory#

In Fig. 3.3, we free the dynamically allocated memory using the delete operator. After the delete operator we write the variable that stores the address on the heap, i.e. p. After this line, p still points to the same address, but that memory is no longer valid for use, and accessing it will lead to undefined behavior. p is now called a dangling pointer. This memory location is “freed” as shown in the image.

Code in main.cpp

Step 3

Fig. 3.3 We free the dynamically allocated memory using the delete operator. This frees the memory back to the heap.#

3.1.1.4. Step 4: Resolve Dangling Pointer#

To resolve the dangling pointer issue, we set p to NULL after freeing the memory. This way, we can check if p is NULL before dereferencing it in the future, preventing undefined behavior.

Code in main.cpp

Step 4

Fig. 3.4 It’s a good practice to always set the pointer to NULL after freeing the memory to avoid dangling pointer issues.#

3.1.1.5. Dynamic Memory Allocation of Arrays#

To dynamically allocate an integer, we use

new int

, but to dynamically allocate an array of integers, we use

new int[size]

, where size is the number of elements in the array. The new operator allocates memory for the array on the heap and returns a pointer to the first element of the array.

For freeing, to free the dynamically allocated integer that was allocated using int* p = new int, we use

delete p;

To free the dynamically allocated array that was allocated using int* p = new int[size], we use

delete[] p;

Notice the [] after delete, which indicates that we are freeing an array. This is important to ensures that entire array is freed.

3.1.2. Explicitly Calling Destructor#

Objects can also be dynamically allocated using the new operator. When an object is created dynamically, its constructor is called. For example, if we have a class named Student, we can dynamically allocate an object and store its address in a pointer p using

Student* p = new Student;

However, to free the memory of the object, the destructor is explicitly called when the delete operator is used. For example, the following line will call the destructor of the Student class:

delete p;

You can try out the following code to see how the constructor and destructor are called when an object is dynamically allocated and freed.

Code in main.cpp


#include <iostream>
using namespace std;

class Student { public: Student() { cout << "Constructor called" << endl; } ~Student() { cout << "Destructor called" << endl; } }; int main(void) { Student* p = new Student; // Dynamically allocated object delete p; // Explicitly calling destructor return 0; }