1

Suppose I have a class:

class ClassX{
   private:
      int* p;
      int i;
   ....
}

And somewhere I do:

ClassX x;
.....
//and in a friend function or in a ClassX function
p = (int*) malloc (.....);

Then when x exit its scope a the destructor is invoked; but it does not free the memory allocated by the malloc right?

And if I redefine it:

ClassX::~ClassX(){ delete [] p; }

it frees the memory allocated by the malloc but not the memory allocated for the class' fields (i.e. i and p)?

Am I missing something?

Thank you.

Aslan986
  • 9,984
  • 11
  • 44
  • 75

6 Answers6

3

The simple rule is: for every malloc there must be exactly one free; for every new, there must be exactly one delete; for every new[] there must be exactly one delete[].

To answer your questions:

[the destructor] does not free the memory allocated by the malloc right?

Correct. It does not. In that case, you have invoked malloc, but never free.

[the modified destructor] frees the memory allocated by the malloc but not the memory allocated for the class' fields?

Wrong. It doesn't free the memory (you must use free, not delete[] in this case. free is mallocs partner. delete[] is new[]'s partner. You can't switch partners.)

Also wrong, it does free the memory allocated for the class's fields (assuming that the fields are non-pointer types.) Freeing the memory occupied by an object occurs after the destructor returns.

Am I missing something?

If you are going to use raw pointers, you also need to understand the Rule of Three. But, better yet, never use raw pointers. Use a smart pointer or a container.

Community
  • 1
  • 1
Robᵩ
  • 163,533
  • 20
  • 239
  • 308
3

First of all, remember these things:

  1. Things allocated by malloc need to be deallocated by free
  2. Don't use malloc
  3. Things allocated by new must be deallocated by delete
  4. Things allocated by new[] must be deallocated by delete[]
  5. One delete for every new and one delete[] for every new[]
  6. Don't use any of the above

Then, you are correct in thinking that, without a destructor, the memory allocated by malloc will not be freed. If you follow the rules above, then you need to use free (not delete[]) to deallocate it:

ClassX::~ClassX() { free(p); }

However, you shouldn't be using malloc in C++ first of all since it doesn't call the constructors of objects, you should use new:

ClassX::ClassX() : p(new int) { }

// NOW we use delete since we used new (not delete[] since we didn't use new[])
ClassX::~ClassX() { delete p; }

However, if you do that, you have to write a copy constructor, copy assignment operator, move constructor, and move assignment operator. So let's look at an even better way:

class ClassX{
private:
    ClassX();

    std::unique_ptr<int> p;
    int i;
    ....
};

// we have to break rule #6 here
ClassX::ClassX() : p(new int) { }

Now you don't even have to write a destructor, you can just let the smart pointer deal with it for you because it will automatically call delete on the thing you made with new when it's destructor is called. Which leads us to...

Your other question:

it frees the memory allocated by the malloc but not the memory allocated for the class' fields (i.e. i and p)?

That's about 1/4 correct. Since i and p are members of the class, they are automatically deallocated when the enclosing class is deallocated, and if they were classes themselves, their destructors would be called. So if you put delete in your destructor, that takes care of the memory allocated by new, and i and p are cleaned up automatically, and everything's good. (You were only 1/4 correct because you used the wrong deallocation function.)

This means that, if you use a smart pointer, the smart pointer's destructor will be called when your object is destructed, and it will automatically deallocates what you allocated with new for you. So you don't even need to worry about it.

This is all assuming you really want to have a dynamic int as part of your class. If you can though, you'd much rather store the int by value (not store a pointer to it) and that way you don't have to mess with smart pointers, deallocation, or any of that other stuff.

Seth Carnegie
  • 73,875
  • 22
  • 181
  • 249
  • Thank for your clear answer. I have a further question: you wrote "they are automatically deallocated when the enclosing class is deallocated". So the deallocation of the enclosing class is not done by the class destructor, right? – Aslan986 May 03 '12 at 15:16
  • @Aslan986 that is absolutely correct. It is done by the caller; if they allocated it with `new`, *they* must deallocate it with `delete`. If they allocated it on the stack, the compiler deallocates it automatically when it goes out of scope. The class's destructor is only meant to deallocate things that were dynamically allocated (with `new` or `new[]`). And after the class's destructor is called, the destructors of all the members are called. Then the whole class is deallocated (like I said above, with `delete` or automatically by the compiler). And all that is done automatically. – Seth Carnegie May 03 '12 at 15:16
1

If you allocate memory manually you must free it, it is not automatically freed for you.

If you call malloc (or calloc, or realloc) you need to free that memory. Similarly, if you new something delete it, and new[] must be matched by delete[].

That being said, you're programming in C++ and there's rarely a good reason to manage memory manually. Make use of smart pointers, std::shared_ptr for shared ownership and std::unique_ptr otherwise.

Then your class looks like this:

#include <memory>

class ClassX{
   private:
      std::unique_ptr<int> p;
      int i;
      ....
};

ClassX::ClassX() : p( new int ) {} /* No need to deallocate p manually */

Note that unique_ptr has a partial specialization for arrays too, so if p points to an array you can allocate memory as follows:

class ClassX{
   private:
      std::unique_ptr<int[]> p;
      int i;
      ....
};

Class::ClassX() : p ( new int[10] ) {}

You could also completely forget about dealing with pointers and use std::vector or std::array, especially for something simple like an array of ints.

Praetorian
  • 106,671
  • 19
  • 240
  • 328
1

You are correct that your destructor should free p, but if you allocate with malloc you should free the memory with free. Alternately you could allocate the memory with:

x.p = new int[size];

This would mean that your delete[] p; destructor would be correct.

With regard to the member variables of the class you don't need to worry about deleting those, if your object was allocated on the stack, they'll be deleted when the stack unwinds. If your class was allocated on the heap, they'll be deleted when you delete x;. Either way, their lifetime is linked to the object that contains them.

Benj
  • 31,668
  • 17
  • 78
  • 127
  • Thank for you answer. But I cant understand what does the destructor does "by default". If I do not redefine it, it just does nothing? – Aslan986 May 03 '12 at 15:07
  • 1
    Correct, by default you have an empty destructor that does nothing. But that does not mean that your member variables will not be deleted. – Benj May 03 '12 at 15:08
1

To simplify, I always tell myself that a pointer IS only a integer. ( it can be 16, 32 or 64 bits long depending on the system). It's a special integer that helps defining an address, but it is only an integer.

So when you construct class X, it allocates the space for two 'integers'. One called p, the other called i. And when the class is destroyed it desallocate the space of two integers and some stuff.

The destructor doesn't bother what you have done with the value of the special integer p ( that represents an address). You could have created memory as you did, or created a random number: it is all the same in the memory representing class X: only an integer.

Stephane Rolland
  • 38,876
  • 35
  • 121
  • 169
1

The rules are simple:

  • A memory allocated with new should be freed with delete.
  • A memory allocated with new [] should be freed with delete [].
  • A memory allocated with malloc() should be freed with free().

Note that you need to pass the exact same address returned by the allocating functions to the deallocating functions.

Good Practices:

  • It is always a good practice to avoid dynamic allocations unless you really need them.
  • If you must, You should use some sort of an smart pointer which suits your requirements, the decision of which smart pointer to use depends on ownership and lifetime semantics involved.
  • In C++ there it is rare that one would need to use malloc and free and don't use them unless you have good reasons to do so,

Also,In your case you also need to follow the Rule of Three in order that your program works correctly.

Community
  • 1
  • 1
Alok Save
  • 202,538
  • 53
  • 430
  • 533