In what cases do I use malloc and/or new?

Viewed 330887

I see in C++ there are multiple ways to allocate and free data and I understand that when you call malloc you should call free and when you use the new operator you should pair with delete and it is a mistake to mix the two (e.g. Calling free() on something that was created with the new operator), but I'm not clear on when I should use malloc/ free and when I should use new/ delete in my real world programs.

If you're a C++ expert, please let me know any rules of thumb or conventions you follow in this regard.

20 Answers

Unless you are forced to use C, you should never use malloc. Always use new.

If you need a big chunk of data just do something like:

char *pBuffer = new char[1024];

Be careful though this is not correct:

//This is incorrect - may delete only one element, may corrupt the heap, or worse...
delete pBuffer;

Instead you should do this when deleting an array of data:

//This deletes all items in the array
delete[] pBuffer;

The new keyword is the C++ way of doing it, and it will ensure that your type will have its constructor called. The new keyword is also more type-safe whereas malloc is not type-safe at all.

The only way I could think that would be beneficial to use malloc would be if you needed to change the size of your buffer of data. The new keyword does not have an analogous way like realloc. The realloc function might be able to extend the size of a chunk of memory for you more efficiently.

It is worth mentioning that you cannot mix new/free and malloc/delete.

Note: Some answers in this question are invalid.

int* p_scalar = new int(5);  // Does not create 5 elements, but initializes to 5
int* p_array  = new int[5];  // Creates 5 elements

From the C++ FQA Lite:

[16.4] Why should I use new instead of trustworthy old malloc()?

FAQ: new/delete call the constructor/destructor; new is type safe, malloc is not; new can be overridden by a class.

FQA: The virtues of new mentioned by the FAQ are not virtues, because constructors, destructors, and operator overloading are garbage (see what happens when you have no garbage collection?), and the type safety issue is really tiny here (normally you have to cast the void* returned by malloc to the right pointer type to assign it to a typed pointer variable, which may be annoying, but far from "unsafe").

Oh, and using trustworthy old malloc makes it possible to use the equally trustworthy & old realloc. Too bad we don't have a shiny new operator renew or something.

Still, new is not bad enough to justify a deviation from the common style used throughout a language, even when the language is C++. In particular, classes with non-trivial constructors will misbehave in fatal ways if you simply malloc the objects. So why not use new throughout the code? People rarely overload operator new, so it probably won't get in your way too much. And if they do overload new, you can always ask them to stop.

Sorry, I just couldn't resist. :)

Always use new in C++. If you need a block of untyped memory, you can use operator new directly:

void *p = operator new(size);
   ...
operator delete(p);

Use malloc and free only for allocating memory that is going to be managed by c-centric libraries and APIs. Use new and delete (and the [] variants) for everything that you control.

Dynamic allocation is only required when the life-time of the object should be different than the scope it gets created in (This holds as well for making the scope smaller as larger) and you have a specific reason where storing it by value doesn't work.

For example:

 std::vector<int> *createVector(); // Bad
 std::vector<int> createVector();  // Good

 auto v = new std::vector<int>(); // Bad
 auto result = calculate(/*optional output = */ v);
 auto v = std::vector<int>(); // Good
 auto result = calculate(/*optional output = */ &v);

From C++11 on, we have std::unique_ptr for dealing with allocated memory, which contains the ownership of the allocated memory. std::shared_ptr was created for when you have to share ownership. (you'll need this less than you would expect in a good program)

Creating an instance becomes really easy:

auto instance = std::make_unique<Class>(/*args*/); // C++14
auto instance = std::unique_ptr<Class>(new Class(/*args*/)); // C++11
auto instance = std::make_unique<Class[]>(42); // C++14
auto instance = std::unique_ptr<Class[]>(new Class[](42)); // C++11

C++17 also adds std::optional which can prevent you from requiring memory allocations

auto optInstance = std::optional<Class>{};
if (condition)
    optInstance = Class{};

As soon as 'instance' goes out of scope, the memory gets cleaned up. Transferring ownership is also easy:

 auto vector = std::vector<std::unique_ptr<Interface>>{};
 auto instance = std::make_unique<Class>();
 vector.push_back(std::move(instance)); // std::move -> transfer (most of the time)

So when do you still need new? Almost never from C++11 on. Most of the you use std::make_unique until you get to a point where you hit an API that transfers ownership via raw pointers.

 auto instance = std::make_unique<Class>();
 legacyFunction(instance.release()); // Ownership being transferred

 auto instance = std::unique_ptr<Class>{legacyFunction()}; // Ownership being captured in unique_ptr

In C++98/03, you have to do manual memory management. If you are in this case, try upgrading to a more recent version of the standard. If you are stuck:

 auto instance = new Class(); // Allocate memory
 delete instance;             // Deallocate
 auto instances = new Class[42](); // Allocate memory
 delete[] instances;               // Deallocate

Make sure that you track the ownership correctly to not have any memory leaks! Move semantics don't work yet either.

So, when do we need malloc in C++? The only valid reason would be to allocate memory and initialize it later via placement new.

 auto instanceBlob = std::malloc(sizeof(Class)); // Allocate memory
 auto instance = new(instanceBlob)Class{}; // Initialize via constructor
 instance.~Class(); // Destroy via destructor
 std::free(instanceBlob); // Deallocate the memory

Even though, the above is valid, this can be done via a new-operator as well. std::vector is a good example for this.

Finally, we still have the elephant in the room: C. If you have to work with a C-library where memory gets allocated in the C++ code and freed in the C code (or the other way around), you are forced to use malloc/free.

If you are in this case, forget about virtual functions, member functions, classes ... Only structs with PODs in it are allowed.

Some exceptions to the rules:

  • You are writing a standard library with advanced data structures where malloc is appropriate
  • You have to allocate big amounts of memory (In memory copy of a 10GB file?)
  • You have tooling preventing you to use certain constructs
  • You need to store an incomplete type

If you have C code you want to port over to C++, you might leave any malloc() calls in it. For any new C++ code, I'd recommend using new instead.

The new and delete operators can operate on classes and structures, whereas malloc and free only work with blocks of memory that need to be cast.

Using new/delete will help to improve your code as you will not need to cast allocated memory to the required data structure.

I had played before with few C/C++ applications for computer graphics. After so many time, some things are vanished and I missed them a lot.

The point is, that malloc and new, or free and delete, can work both, especially for certain basic types, which are the most common.

For instance, a char array, can be allocated both with malloc, or new. A main difference is, with new you can instantiate a fixed array size.

char* pWord = new char[5]; // allocation of char array of fixed size 

You cannot use a variable for the size of the array in this case. By the contrary, the malloc function could allow a variable size.

int size = 5; 
char* pWord = (char*)malloc(size); 

In this case, it might be required a conversion cast operator. For the returned type from malloc it's a pointer to void, not char. And sometimes the compiler could not know, how to convert this type.

After allocation the memory block, you can set the variable values. the memset function can be indeed slower for some bigger arrays. But all the bites must be set first to 0, before assigning a value. Because the values of an array could have an arbitrary content.

Suppose, the array is assigned with another array of smaller size. Part of the array element could still have arbitrary content. And a call to a memset function would be recomended in this case.

memset((void*)pWord, 0, sizeof(pWord) / sizeof(char)); 

The allocation functions are available for all C packages. So, these are general functions, that must work for more C types. And the C++ libraries are extensions of the older C libraries. Therefore the malloc function returns a generic void* pointer. The sructures do not have defined a new, or a delete operator. In this case, a custom variable can be allocated with malloc.

The new and delete keywords are actually some defined C operators. Maybe a custom union, or class, can have defined these operators. If new and delete are not defined in a class, these may not work. But if a class is derived from another, which has these operators, the new and delete keywords can have the basic class behavior.

About freeing an array, free can be only used in pair with malloc. Cannot allocate a variable with malloc, and then free with delete.

The simple delete operator references just first item of an array. Because the pWord array can be also written as:

pWord = &pWord[0]; // or *pWord = pWord[0]; 

When an array must be deleted, use the delete[] operator instead:

delete[] pWord; 

Casts are not bad, they just don't work for all the variable types. A conversion cast is also an operator function, that must be defined. If this operator is not defined for a certain type, it may not work. But not all the errors are because of this conversion cast operator.

Also a cast to a void pointer must be used when using a free call. This is because the argument of the free function is a void pointer.

free((void*)pWord); 

Some errors can arise, because the size of the array is too small. But this is another story, it is not because of using the cast.

With kind regards, Adrian Brinas

Related