Aller au contenu

Memory Management: Deallocation and Destructors

The Delete Keyword

As already mentioned, an important difference between Java and C++ is the concept of releasing an object and its destruction.

In Java, the Garbage Collector is responsible for releasing memory. This means that programmers may not explicitly deallocate objects. Programmers may define a finalize() method for each class, which will be called by the Garbage Collector upon releasing the object.

In C++, the programmer must explicitly call the delete operator. Upon calling delete, the destructor of the object class will be called.

The delete keyword must be used whenever the new keyword is used and the object is created on the heap because the memory management is left to the programmer’s care. When an object is allocated as an array with new int[2] for instance, then the programmer must deallocate the object with the delete [] statement.

class Dummy {
public:
    Dummy() { }
};

int main() {
    // Delete an object
    Dummy* d = new Dummy();
    delete d;
    d = nullptr;

    // Delete an array
    constexpr int N = 2;
    Dummy* dummies = new Dummy[N];
    for (int i = 0; i < N; i++) {
        dummies[i] = Dummy();
    }
    delete[] dummies;
    dummies = nullptr;

    return 0;
}

The Destructor of a Class

Upon calling delete, the destructor of the object class will be called. The concept of a class destructor is illustrated in the updated Point class. In this example, the destructor doesn’t have meaningful statements, but a destructor can contain instructions like the constructor does.

#include <iostream>

class Point {
public:
    // constructors
    Point() : _x(0), _y(0) {
        std::cout << "Point default constructor called" << std::endl;
    }
    Point(float x, float y) : _x(x), _y(y) {
        std::cout << "Point constructor called" << std::endl;
    }

    // destructor
    ~Point() {
        std::cout << "Point destructor called" << std::endl;
    }

    // public methods
    void move(float dx, float dy) {
        _x += dx;
        _y += dy;
    }
    float x() { return _x; }
    float y() { return _y; }

private:
    // private data fields
    float _x;
    float _y;
};

int main() {
    std::cout << "Begin of the program" << std::endl;
    Point p1(1, 2);

    Point* p2 = new Point(2, 3);
    delete p2;
    p2 = nullptr;

    std::cout << "End of the program" << std::endl;

    return 0;
}

Key Points About Destructors

  • Unlike a constructor, a destructor does not take any argument. So a destructor cannot be overloaded. There may be several constructors for a class, but there may be only a single destructor.
  • Like a constructor, a destructor does not return any value.

When Destructors Are Called

The destructor is called whenever an object’s lifetime ends, which includes in particular:

  • The use of the delete expression, for objects with dynamic storage duration.
  • End of scope, for objects allocated on the stack.

Smart Pointers

C++ provides smart pointers as a safer alternative to raw pointers for automatic memory management:

  • std::unique_ptr: Owns exclusive memory
  • std::shared_ptr: Allows multiple owners of the same memory

These automatically deallocate memory when they go out of scope, reducing the risk of memory leaks.

#include <memory>

void function()
{
    // solution with raw pointer
    char* pArray = new char[100];
    // do something with pArray

    // say that we have a
    bool condition = false;
    if (condition) {
        // at this point, a memory leak arises since pArray is not released
        return;
    }

    // call delete []
    delete [] pArray;
    pArray = nullptr;

    // solution with unique_ptr
    // memory will be automatically released when array_ptr is removed from the stack
    std::unique_ptr<char> array_ptr = std::unique_ptr<char>(new char[99]);
    // do something with array_ptr

    if (condition) {
        // no memory leak since the destructor of array_ptr is called
        // and memory is thus released
        return;
    }
}

int main()
{
    function();
    return 0;
}