Well, that's why C++ is unmanaged language - you have to manage everything manually. And when you do so, you can make mistakes.
The list of common mistakes made by software engineers when working with dynamic memory, includes (but is not limited to):
- Memory leak:
When memory block (or object) is allocated, and persist in memory even if it can't be used anymore. For example, the program looses a pointer to this memory, without notifying the system about unused memory. This results in continuous expansion of memory reserved by an application. And will definitely make any system to run out of memory (if provided enough time). - Memory alloc/dealloc operator mismatch:
If you allocate with malloc/calloc, you have to call free for this pointer
For an object, created with operator new, operator delete should be called.
And for arrays of objects, we have new[] and delete[] operators.
But you may run in unpredictible troubles if you do mix these types of allocation/deallocation for the same pointer. - Multiple deallocation of the same memory block.
- Deallocation of memory, that was not previously allocated.
- Access to memory outside bounds of allocated blocks.
And there are lots of chances for such things to happen in some very complex and hidden way.
There are also many ways to avoid such problems.
- Understand C++ in the nutshell an how memory system works.
- Use "smart" pointers
- Use some utilities to profile and verify memory operations (there are lots of them)
You can just overload and redefine allocation/deallocation routines and make them track lost blocks and even alloc/dealloc method mismatch.
So this is how I did it:
MLD.h:
#if !defined(MEMORY_LEAK_DETECTOR) && defined(_DEBUG) #define MEMORY_LEAK_DETECTOR #include <stddef.h> #include <stdlib.h> #include <stdio.h> #include <memory.h> // Standard C functions extern "C" { void* _mld_malloc(size_t aSize, const char* aFile, int aLine); void* _mld_calloc(size_t aNum, size_t aSize, const char* aFile, int aLine); void* _mld_realloc(void* aPtr, size_t aSize, const char* aFile, int aLine); void _mld_free(void* aPtr, const char* aFile, int aLine); #if !__STDC__ char* _mld_strdup(const char* s, const char* aFile, int aLine); #endif void _mld_dump(); } #ifdef __cplusplus // C++ global allocator/deallocator operators extern "C++" { void* operator new(size_t aSize, const char* aFile, int aLine); void* operator new[](size_t aSize, const char* aFile, int aLine); void operator delete(void* aPtr, const char* aFile, int aLine); void operator delete[](void* aPtr, const char* aFile, int aLine); void operator delete(void* aPtr); void operator delete[](void* aPtr); } #endif #ifndef MLD_INTERNAL_LOCK // This should be checked to disable overloaded new and delete operators #define DMALLOC // Redefine functions to get file and line info about allocation #define malloc(aSize) _mld_malloc(aSize, __FILE__, __LINE__) #define calloc(aNum, aSize) _mld_calloc(aNum, aSize, __FILE__, __LINE__) #define realloc(aPtr, aSize) _mld_realloc(aPtr, aSize, __FILE__, __LINE__) #define free(aPtr) _mld_free(aPtr, __FILE__, __LINE__) #define strdup(aStr) _mld_strdup(aStr, __FILE__, __LINE__) #define _strdup(aStr) _mld_strdup(aStr, __FILE__, __LINE__) #define new new(__FILE__, __LINE__) #endif #endif // MEMORY_LEAK_DETECTOR
MLD.cpp:
#define MLD_INTERNAL_LOCK #include "MemoryLeakDetector.h" #undef malloc #undef calloc #undef realloc #undef free #undef strdup #undef new #undef delete #ifdef _DEBUG class CMld { public: enum EAllocType { eUnused, eMalloc, eNew, eArrayNew, eAllocTypeQty }; struct MemoryBlockInfo { const char* mFile; unsigned mLine; size_t mSize; void* mPointer; EAllocType mType; MemoryBlockInfo() : mFile ("") , mLine (0) , mSize (0) , mPointer (NULL) , mType (eUnused) { } MemoryBlockInfo(const char* aFile, unsigned aLine, void* aPtr, size_t aSize, EAllocType aType) : mFile (aFile) , mLine (aLine) , mSize (aSize) , mPointer (aPtr) , mType (aType) { } }; enum EConstatnts { eBlocksQty = 10*1024 }; public: ~CMld() { Dump(); } void Dump() { printf("------------ Memory Leak Detector Report --------------\n"); size_t totalSize = 0; for(int i=0; i < eBlocksQty; i++) { const MemoryBlockInfo& it = mBlocks[i]; if(it.mType != eUnused) { totalSize += it.mSize; printf("%p %s:%d (%d bytes)\n", it.mPointer, it.mFile, it.mLine, it.mSize); } } printf("Total leaked: %d bytes\n", totalSize); printf("---------------------------------------------------------\n"); } void TrackAllocation(const char* aFile, unsigned aLine, void* aPtr, size_t aSize, EAllocType aType) { for(int i=0; i < eBlocksQty; i++) { if(mBlocks[i].mType == eUnused) { mBlocks[i] = MemoryBlockInfo(aFile, aLine, aPtr, aSize, aType); return; } } } void TrackDeallocation(const char* aFile, unsigned aLine, void* aPtr, EAllocType aType) { for(int i=0; i < eBlocksQty; i++) { const MemoryBlockInfo& it = mBlocks[i]; if(it.mType != eUnused && it.mPointer == aPtr) { if (it.mType != aType) { printf("Warning: Wrong alloc/free combination of address %p\n", aPtr); printf(" %s at %s:%d\n", AllocTypeStr[it.mType], it.mFile, it.mLine); printf(" %s at %s:%d\n", DeallocTypeStr[aType], aFile, aLine); } mBlocks[i].mType = eUnused; return; } } printf("Warning: Deallocation of unknown address %p\n", aPtr); printf(" %s at %s:%d\n", DeallocTypeStr[aType], aFile, aLine); } MemoryBlockInfo* FindBlock(void* aPtr) { for(int i=0; i < eBlocksQty; i++) { if(mBlocks[i].mPointer == aPtr) { return &mBlocks[i]; } } return NULL; } private: static const char* AllocTypeStr[eAllocTypeQty]; static const char* DeallocTypeStr[eAllocTypeQty]; MemoryBlockInfo mBlocks[eBlocksQty]; }; static CMld __mld; const char* CMld::AllocTypeStr[eAllocTypeQty] = { "unused", "malloc", "new", "new[]" }; const char* CMld::DeallocTypeStr[eAllocTypeQty] = { "unused", "free", "delete", "delete[]" }; void* _mld_malloc(size_t aSize, const char* aFile, int aLine) { void* aPtr = malloc(aSize); __mld.TrackAllocation(aFile, aLine, aPtr, aSize, CMld::eMalloc); return aPtr; } void* _mld_calloc(size_t aNum, size_t aSize, const char* aFile, int aLine) { void* aPtr = calloc(aNum, aSize); __mld.TrackAllocation(aFile, aLine, aPtr, aSize * aNum, CMld::eMalloc); return aPtr; } void* _mld_realloc(void* aPtr, size_t aSize, const char* aFile, int aLine) { void* nptr = realloc(aPtr, aSize); if(nptr!=NULL && aPtr==NULL && aSize>0) // block allocated { __mld.TrackAllocation(aFile, aLine, aPtr, aSize, CMld::eMalloc); } else if(nptr==NULL && aPtr!=NULL && aSize == 0) // block deallocated { __mld.TrackDeallocation(aFile, aLine, aPtr, CMld::eMalloc); } else if(nptr!=NULL) // block modified { CMld::MemoryBlockInfo* info = __mld.FindBlock(aPtr); info->mPointer = nptr; info->mFile = aFile; info->mLine = aLine; info->mSize = aSize; info->mType = CMld::eMalloc; } return nptr; } char* _mld_strdup(const char* str, const char* aFile, int aLine) { #ifdef WINCE char* str2 = _strdup(str); #else char* str2 = strdup(str); #endif __mld.TrackAllocation(aFile, aLine, str2, strlen(str), CMld::eMalloc); return str2; } void _mld_free(void* aPtr, const char* aFile, int aLine) { __mld.TrackDeallocation(aFile, aLine, aPtr, CMld::eMalloc); free(aPtr); } void* operator new(size_t aSize, const char* aFile, int aLine) { void* aPtr = malloc(aSize); __mld.TrackAllocation(aFile, aLine, aPtr, aSize, CMld::eNew); return aPtr; } void* operator new[](size_t aSize, const char* aFile, int aLine) { void* aPtr = malloc(aSize); __mld.TrackAllocation(aFile, aLine, aPtr, aSize, CMld::eArrayNew); return aPtr; } void operator delete(void* aPtr, const char* aFile, int aLine) { __mld.TrackDeallocation(aFile, aLine, aPtr, CMld::eNew); free(aPtr); } void operator delete[](void* aPtr, const char* aFile, int aLine) { __mld.TrackDeallocation(aFile, aLine, aPtr, CMld::eArrayNew); free(aPtr); } void operator delete(void* aPtr) { __mld.TrackDeallocation("unknown", 0, aPtr, CMld::eNew); free(aPtr); } void operator delete[](void* aPtr) { __mld.TrackDeallocation("unknown", 0, aPtr, CMld::eArrayNew); free(aPtr); } void _mld_dump() { __mld.Dump(); } #endif
Now everything you need is to include the header in every cpp file in our project. You can do it easily using a precompiled header in MSVC, or an "-include" flag for GCC.
The above example successfully handles:
- Operator mismatches
- Double free
- Memory leaks
- You can see some false warnings, if you have some static objects or singletons.
- In this implementation, I have a limited max amount of objects (can be avoided using lists)
You can even make some checks for boundaries consistency! :)
Invent! ;)
Bet on the NFL | NJVGM.COM | JT Marriott Hotel & Casino
ВідповістиВидалитиVisit JT Marriott Hotel & 오산 출장마사지 Casino NJ 제주 출장마사지 and enjoy a stay in one 전라북도 출장안마 of the 세종특별자치 출장샵 hotel's 544 guestrooms. Book 이천 출장안마 direct with JT Marriott and enjoy free Wi-Fi in the