This blog is subject the DISCLAIMER below.

Friday, January 26, 2007

Advanced C++ part 6 : Advanced Memory Management part 2 : malloc and new, what's the difference?

In the days of C, dynamic memory allocation was performed through the malloc() function. You just specify the number of bytes, and it returns a pointer to the allocated memory. If the memory allocation fails it will return NULL. malloc might not initialize the allocated memory.

As C++ introduced objects, there were different requirements for dynamic allocation. In old C, you usually allocate space for homogeneous arrays of the same primitive types. Even for arrays of structs, you would usually initialize them with default values, not values dependant a result of some sort of computations. In C++, constructors added the encapsulation of the initialization logic. malloc won't call any constructors, it just allocates plain bytes.

Operator new, has more information that available to malloc. It knows the type. Which enables that operator to know the size of a single objects, thus releasing you from the burden of calculating how many bytes per object you need to allocate; you just use the number of objects you need. It also makes new able to call the constructor of that type on the newly allocated memory.

In malloc you'd have to check every pointer returned by it against NULL to know if the allocation have succeeded. But in new, it does throw an exception (you can make it return NULL instead)[1]. Example:

MyClass* m = new MyClass();
catch(bad_alloc x)
// report an error
or you can simply do the following to prevent exception being thrown:
MyClass* m = new (std::nothrow) MyClass();
if(m == NULL)
// report an error
[1]: Microsoft implementation of new doesn't throw an exception as opposed to the standard. It does return NULL.

There is another thing you can do to handle failed allocations. There is a function that new calls when it fails to allocate. It's type [2] is new_handler; which is defined as a function that takes no parameters and returns void. The default new_handler is the one that throws the bad_alloc exception. (Thus you can modify that behaviour). To set your new_handler as the one used, you can set_new_hanlder( your_new_handler ). This function returns the old new_handler. Your new_handler is expected to do one of 3 things:
  1. Call abort() or exit()
  2. Throw bad_alloc or a type inherited from it
  3. Make more memory available for allocation by some means
[2]: function pointer type

Operator new and delete automatically call the constructor and the destructor, respectively. We will talk in a forthcoming article about some variants where you must call the destructor yourself or where you can prevent new from automatically calling the constructor so you can call it manually as an optimization in case of very large arrays.

Note: we will take later about details of exceptions and namespaces, just take it now "as is" or read about it in some reference.

Next article in this sub-series isA will talk about overriding new and delete and the reasons why you might need that practically.


No comments: