Introduction
Memory management is very critical and hence an interesting topic in C/C++. In C, malloc and free are present. In C++, operator new
and operator delete
are provided, which give better control.
Operator new
and operator delete
internally call the same old malloc
/free
. But they are called by new operator and delete operator, which calls object constructor and destructor for initialization and clean up.
Intermixing new
/delete
and malloc
/free
should be avoided. In such cases, results are undefined and messy.
operator new
This operator internally calls malloc
, and initializes object from memory. A simple form of this operator is:
static void* operator new(size_t size)
{
FreeStore* p=(FreeStore*)malloc(sizeof(FreeStore));
return p;
}
operator delete
This operator internally calls free
:
static void operator delete(void *p)
{
free(p);
}
Placement new
The special operator new does nothing and is used to construct object from raw memory. It is simply implemented as:
void *operator new (size_t, void *p)
{
return p;
}
First is irrelevant, but mandatory. And this operator returns a memory address passed to it. Later it is used for object construction. You can see this definition in <new.h>
.
Placement delete
In a class for every form of new, there should be a corresponding delete present. If you define operator placement new in your class and forget to define the corresponding placement delete, the compiler will remind you. A simple form of placement delete is:
static void operator delete(void*,void *)
{
return;
}
operator new[] and operator delete[]
When allocating memory for an array of objects, things become interesting. Consider the following code snippet:
FreeStore* pArrayOfObject=new FreeStore[1];
delete [] pArrayOfObject;
In the above code, operator new[]
and operator delete[]
are used. Now we'll discuss methods used for implementing these operators. How is the track of array size kept.
Methods Used
There are two commonly used methods to achieve this:
- Keep information about size of array, for that allocates extra memory.
- Keep a global map, where pointer is stored as key, and its array size as value.
Here is a brief explanation of how it can be achieved.
1. Book Keeping
As said earlier, allocate enough memory to hold extra information, i.e. of size of array. While deleting that array, unwrap that memory and use for deletion. In my example, I used 4 bytes for storing extra information. It is declared as:
const size_t BOOKKEEPING_SIZE=4;
Class FreeStore_BookKeep
is used to demonstrate this.
I've used static
method operator_new
which would be called to allocate array of elements, it takes argument size_t
, i.e., number of elements to be allocated.
This method would be like this.
size_t* ptmpObject = (size_t*) malloc(BOOKKEEPING_SIZE + nsize *
sizeof(FreeStore_BookKeep));
*ptmpObject=size;
FreeStore_BookKeep* pThis = (FreeStore_BookKeep*) ( ptmpObject + BOOKKEEPING_SIZE) ;
size_t i(0);
for (i ; i < size; ++i)
{
new (pThis+i) FreeStore_BookKeep();
}
return pThis;
In the above method, memory allocated is more than what is requested. For deleting this array, static
method operator_delete
is used, while takes void*
as parameter. i.e. pointer to delete memory. This method uses same information kept in extra bytes, to free the memory. Its implementation is like this,
size_t *ptemp=(size_t*)pMemoryToDelete-BOOKKEEPING_SIZE;
size_t size=(size_t)(*ptemp);
FreeStore_BookKeep *pThis=(FreeStore_BookKeep*)(pMemoryToDelete);
for (size_t i=0; i<size;i++)
{
(pThis+i)->~FreeStore_BookKeep();
}
free(ptemp);
The main drawback of this method is, if user doesn't use delete[]
(operator_delete
in this case), and uses normal delete
on pointer, then number of BOOKKEEPING_SIZE
bytes will never be freed.
2. Using Global Map
Use global map to store information about pointer and number of elements, i.e. while allocating memory, store pointer allocated as key, and number of elements as value, in global map. And while deleting memory, access the same map to find the number of elements allocated.
Class FreeStore_Map
is used to demonstrate this. Global map is declared as:
typedef map<void*,size_t> MemoryInfo;
MemoryInfo gKeepSizeTrack;
New operator_new
method will look like this:
FreeStore_Map *pThis = (FreeStore_Map*) malloc(nsize * sizeof(FreeStore_Map));
size_t i(0);
for (i ; i < nsize; ++i)
{
new (pThis+i) FreeStore_Map();
}
gKeepSizeTrack[pThis]=nsize;
return pThis;
Now, while deleting memory, the same map is accessed. New operator_delete
will look like this:
MemoryInfo::iterator it=gKeepSizeTrack.find(p);
size_t nsize=it->second;
FreeStore_Map* pTemp=(FreeStore_Map*)p;
for (size_t i=0; i<nsize;i++)
{
(pTemp+i)->~FreeStore_Map();
}
gKeepSizeTrack.erase(p);
free(p);
operator_new
and operator_delete
need to be static
in nature. Because operator new
is the method that initializes/constructs class calling constructor and operator delete
destroys object, these methods need to be static
in nature.
Using this Code
The main function of code supplied looks like this:
FreeStore_BookKeep *pObjArr=FreeStore_BookKeep::operator_new(100);
FreeStore_BookKeep::operator_delete(pObjArr);FreeStore_Map *pObjArr1=FreeStore_Map::operator_new(200);
FreeStore_Map::operator_delete(pObjArr1);
References
- MSDN article "Handling Exceptions, Part 5", By Robert Schmidt
History
- Sept 14, 2006: Published
- Sept 17, 2006: Updated
- Sept 21, 2006: Updated