Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What are the dynamics of the C++ delete statement?

This is merely for curiosity sake because I have not used new and delete in c++ except for the most basic uses.

I know that delete releases memory. The thing I'm wondering is how does it handle more complex cases?

For instance, if I have a user-defined class like this:

class MyClass
{
public:
    MyClass();
    ~MyClass()
    {
        delete [] intArray;
    }
    //public members here
private:
    int* intArray;
};

Assuming the class allocates memory somehow for intArray, and then release it in the destructor, What if I used the class like this: MyClass* myClass = new MyClass(); and released it later with delete myclass;

How does delete handle the releasing of all the memory? Does the class destructor get called first to release all of the memory allocated by the class (ie int* intArray) and then release the memory allocated to hold the class?

What if I had a class like this:

class MyClass
{
public:
    MyClass();
    ~MyClass()
    {
        delete anotherMyClass;
    }
    //public members here
private:
    MyClass* anotherMyClass;
};

Assuming anotherMyClass is not allocated with the constructor, which would use up memory very quickly, what if there was a chain of MyClasses attached to each other like a linked-list? Would the delete statement in the destructor work in this case? Would each anotherMyClass be recursively released when the destructor gets called?

Are there any specific weird tricks or caveats with the new and delete statements that you know about?

like image 457
Mike Webb Avatar asked Dec 21 '22 20:12

Mike Webb


2 Answers

delete intArray;

I assume intArray points to the first element of an int array? In that case, delete intArray yields undefined behavior. If you allocate via new[], you must release via delete[].

delete[] intArray;

Yes I know, delete intArray might appear to work just fine on certain systems with certain compiler versions under certain compiler options -- or it might not. That's undefined behavior for ya.

Also, you should follow the Rule of Three. Defining your own destructor but relying on the implicitly-defined copy constructor and copy assignment operator is a recipe for disaster.

like image 38
fredoverflow Avatar answered Dec 31 '22 01:12

fredoverflow


Given a pointer (p) to a dynamically allocated object, delete does two things:

  1. It calls the destructor of the dynamically allocated object. Note that when ~MyClass() completes, the destructors for any member variables of class type are called.
  2. It frees the memory occupied by the dynamically allocated object.

It doesn't search the member variables of the object for other pointers to free; it doesn't free any other memory and doesn't do anything else.

If you need to free the memory pointed to by intArray, you need to delete it in the destructor of MyClass.

However, in almost all C++ code, you don't need to worry about this. You should be using smart pointers like shared_ptr, unique_ptr, auto_ptr, and scoped_ptr to automatically manage dynamically allocated objects. Manual resource management is difficult at best and should be avoided wherever possible.

This is part of a broader idiom, Scope-Bound Resource Management (SBRM, also called Resource Acquisition is Initialization, or RAII). This is by far the most important design pattern to understand and to use everywhere in your C++ code.

If in your class you had declared this instead:

boost::scoped_ptr<int> intArray;

then when the scoped_ptr<int> object is destroyed, it will free the pointer that it holds. You then do not have to do any work to manually destroy the object.

In well-written, modern C++ code, you should rarely need to manually use delete. Smart pointers and other SBRM containers should be used to manage any type of resource that needs cleanup, including dynamically allocated objects.


In your second example, given a linked list that looks like:

x -> y -> z -> 0

you would have an order of operations that looks like this:

delete x;
  x.~MyClass();
    delete y;
      y.~MyClass();
        delete z;
          z.~MyClass();
            delete 0;
          [free memory occupied by z]
      [free memory occupied by y]
  [free memory occupied by x]

The objects in the linked list would be destroyed in reverse order.

like image 62
James McNellis Avatar answered Dec 31 '22 00:12

James McNellis