10

Quick question; I've googled around and found some answers already, but I'm a bit paranoid so I want to be sure.

Consider this situation:

struct CoordLocation
{
    float X;
    float Y;
    float Z;
};

int main()
{
    CoordLocation *coord = new CoordLocation();
    delete coord;
    return 0;
}

Will calling delete also clear the memory used by the fields X, Y, Z? Some answers I found mentioned that I'd just delete the POINTER, not the actually referenced object this way. What if...

struct CoordLocation
{
    float *X;
    float *Y;
    float *Z;
};

int main()
{
    CoordLocation *coord = new CoordLocation();
    delete coord;
    return 0;
}

And what if I manually free the memory for each object inside the struct's constructor/destructor?

struct CoordLocation
{
    CoordLocation()
    {
         *X = new float;
         *Y = new float;
         *Z = new float;
    }
    ~CoordLocation()
    {
         delete X; delete Y; delete Z;
    }
    float *X;
    float *Y;
    float *Z;
};

int main()
{
    CoordLocation *coord = new CoordLocation();
    delete coord;
    return 0;
}

I noticed that for a simple situation such as:

   float *a = new float;
   *a = 5.0f;
   printf("%f", *a);
   delete a;
   printf("%f", &a);

printf would print 5.0, so the variable pointed to by a is not exactly destroyed.

So my question is: How can I reliably free (as in no memory leaks) ALL the memory used by the struct in this case?

struct CoordLocation
{
    float X;
    float Y;
    float Z;
};

int main()
{
    CoordLocation *coord = new CoordLocation();
    delete coord;
    return 0;
}

Thanks!

  • Your second `printf` is a bit wrong (format identifier does not match argument type). Did you actually want to write `printf("%f", *a);`, like in the first `printf`? – Bojan Komazec Apr 20 '12 at 20:20
  • If you're using a recent/modern compiler, the most reliable way to ensure that **new**'ed memory will be correctly deleted is to use one of the "smart" pointer types available in the C++ standard library. std::unique_ptr and std::shared_ptr take complete ownership of a new'ed object and guarantee that the object they're responsible for won't be leaked. (Ideally you'll never have any _raw_ pointers anywhere in your code) – Ben Cottrell Apr 20 '12 at 20:20
  • Thanks for the tip on the unique_ptr. I'm working with Visual C++ 2008 so the compiler does not support the new standard (can't find unique_ptr in the std namespace). I'll go with the old ways, at least until later on. –  Apr 20 '12 at 21:38
  • Yeah, my mistake on the 2nd printf :) –  Apr 20 '12 at 21:38

3 Answers3

24

You only need to delete memory you allocate with new.

printf would print 5.0, so the variable pointed to by a is not exactly destroyed.

You're actually running into undefined behavior. Although the value is still there, the memory was released and can be reused.

So the following:

struct CoordLocation
{
    float X;
    float Y;
    float Z;
};

can't create a memory leak if you omit the destructor.

Your next snippet:

struct CoordLocation
{
    float *X;
    float *Y;
    float *Z;
};

int main()
{
    CoordLocation *coord = new CoordLocation();
    delete coord;
    return 0;
}

can potentially create a memory leak, but not as it is. The following will:

int main()
{
    CoordLocation *coord = new CoordLocation();
    coord->X = new float();
    delete coord;
    return 0;
}

Your third example

struct CoordLocation
{
    CoordLocation()
    {
         *X = new float;
         *Y = new float;
         *Z = new float;
    }
    ~CoordLocation()
    {
         delete X; delete Y; delete Z;
    }
    float *X;
    float *Y;
    float *Z;
};

int main()
{
    CoordLocation *coord = new CoordLocation();
    delete coord;
    return 0;
}

won't create a memory leak because you free all the memory that you allocate. If you were to omit the destructor or forget to call delete coord;, they you'd have a memory leak.

A good rule of thumb: call a delete for every new and a delete[] for every new[] and you're safe.

Luchian Grigore
  • 253,575
  • 64
  • 457
  • 625
  • 1
    Perhaps, "WILL be reused." Said in a threatening tone of voice, while holding a lit flashlight under your chin. – mjfgates Apr 20 '12 at 20:16
  • Luchian, what happens in third example if second or third `new` throws exception? There are no exception handlers in `main` so `terminate ` and `abort` will be executed and program will be terminated but prior to that, isn't there a memory leak as `delete coord;` (and so `delete X;`) is never executed? – Bojan Komazec Apr 20 '12 at 20:33
  • @BojanKomazec AH! It depends on what you mean by a memory leak - http://stackoverflow.com/questions/9921590/is-not-calling-delete-on-a-dynamically-allocated-object-always-a-memory-leak – Luchian Grigore Apr 20 '12 at 20:36
  • The third example will create a double delete if you ever copy construct one. – Mark B Apr 20 '12 at 20:36
  • @LuchianGrigore Yeah, that's one of those border cases. It might or might not be regarded as a leak; am still reading answers. You raised very interesting question there. – Bojan Komazec Apr 20 '12 at 20:55
  • Amazing answer! You should write tutorials ;) – Piotr Dabkowski Sep 07 '17 at 19:42
0

In this example:

struct CoordLocation
{
    float X;
    float Y;
    float Z;
};

int main()
{
    CoordLocation *coord = new CoordLocation();
    delete coord;
    return 0;
}

The memory is freed. There are no leaks in that code. On the example you gave, where your struct contained pointers, as long as you free them in the destructor(as you did in your example), there will be no leaks.

In this snippet:

float *a = new float;
*a = 5.0f;
printf("%f", *a);
delete a;
printf("%f", &a);

The value of a stays the same after the delete, since delete(ing) a pointer will only mark a memory address as "freed", it will not modify its contents. Accessing freed pointers is undefined behaviour.

mfontanini
  • 21,410
  • 4
  • 65
  • 73
0

The pointer itself is allocated with automatic storage duration, there is nothing to free there. The struct is those three fields, which are freed when you call delete. You only call delete on something that was returned by new.

When you allocate something you allocate enough memory to hold it, which means enough memory to hold all of its fields (and some housekeeping memory that is implementation specific, but you don't worry about that). When you delete it you free the same amount of memory that you allocated.

One thing to note however is a case like this (in C++ you wouldn't create a type like this, but the example is relevant):

struct Foo {
    char *str;
};

Foo *f = new Foo();
f->str = new char[10];

delete f;

In this case you have a leak. You delete f, which consists of enough memory to hold a single char*, but what that char* points to has been dynamically allocated as well. So, you need to free it too:

delete f->str;
delete f;

Again, in C++ you would probably not design a type this way, instead favoring types like std::string and principles such as RAII, but the example is relevant.

Ed S.
  • 122,712
  • 22
  • 185
  • 265