tags:

views:

160

answers:

3

I have implemented a custom allocator (to be used by STL containers within my memory debugging utility, without them using my overridden new operator). Within the memory debugger I use an instance of the same allocator class to allocate the objects I need to keep track of 'normal' memory allocations. It's all working fine, but I'm not sure if the way I'm using the allocator interface is correct. Here are the utility methods as they currently stand (correct initialization parameters for the entry will be added soon):

iidebug::CMemoryDebugger::CEntry* iidebug::CMemoryDebugger::NewEntry()
{
    CEntry* pEntry = m_entryAllocator.allocate(1);
    if (0 != pEntry)
    {
     new(pEntry) CEntry(0, 0, __FILE__, 0, 0, 0);
    }
    return pEntry;
}

void iidebug::CMemoryDebugger::DeleteEntry( iidebug::CMemoryDebugger::CEntry* pEntry )
{
    if (0 != pEntry)
    {
     destruct(pEntry);
     m_entryAllocator.deallocate(pEntry, 1);
    }
}

This just seems very messy, but I can't see how I can improve it.

A: 

What is destruct? I suppose it should be:

void iidebug::CMemoryDebugger::DeleteEntry( iidebug::CMemoryDebugger::CEntry* pEntry )
{
    if (0 != pEntry)
    {
        pEntry->~CEntry();
        m_entryAllocator.deallocate(pEntry, 1);
    }
}
Kirill V. Lyadvinsky
Sorry about that, destruct is one of three helper functions implemented to handle destruction as in http://www.codeguru.com/cpp/cpp/cpp_mfc/stl/article.php/c4079
FlintZA
+1  A: 

You can actually overload new and delete to take an allocator parameter, like so:

inline void *operator new(size_t sizeT, Allocator *&a) {
  return a->allocate(sizeT);
}

inline void operator delete(void * mem, Allocator *&a) {
  a->release(mem);
}

int main()
{
  Allocator * a = new Allocator;
  C *c = new(a) C;
  c->~C();
  operator delete(c, a);
  return 0;
}

See the wikipedia article for more detail. It's still a bit messy because you have to be sure not to call the regular delete operator if your allocator does something special.

Drew Hoskins
That's a much neater option, thanks a stack :)
FlintZA
Something I like to do is also override the default new operator so that all allocations have a function pointer at the head of the allocation (need to add an extra 4 bytes if working on a 32bit x86 app), which points to the function to deallocate the memory. then when you delete the object (all objects can be deleted via the default delete operators with this), you just call that function pointer.
Grant Peters
Interesting idea, is this for memory debugging? If it's in the default new, and the same function pointer is always passed in, why not just call the function explicitly inside the overridden delete instead of taking the extra 4 bytes on each pointer?
FlintZA
its in all new calls (default and overloaded), this means you only need to call the default delete, which then just calls the specific deallocate function required, thus bypassing the problem of calling mismatched new/delete functions
Grant Peters
Ah ok, makes sense. I'll keep that in mind for future cases but for this one Drew's solution is just what I needed.
FlintZA
+1  A: 

Just for reference in case anyone struggles to use Drew's code, it needed a few tweaks. The following is what I ended up using:

template <typename T>
void* operator new(SizeT iSize, SimpleAllocator<T>& rAllocator)
{
    return rAllocator.allocate(1);
}

template <typename T>
void operator delete(void* pvMemory, SimpleAllocator<T>& rAllocator)
{
    ((T*)pvMemory)->~T();   
    rAllocator.deallocate(pvMemory, 1);
}

And actually using it is as simple as:

// SimpleAllocator<CFoo> fooAllocator = ...
CFoo* pFoo = new(fooAllocator) CFoo(param1, param2, ...); 
// Do stuff with pFoo...
operator delete(pFoo, fooAllocator);
FlintZA