
Prosta metoda na wykrycie wycieków pamięci w C++ (memory leak) wykorzystującej
przeładowanie operatorów new, new[] i delete, delete[].
new,
new[] będziemy tworzyli także blok struct SMemList z informacją na temat wielkości rezerwowanego obszaru, flagi tablica czy nie, ew. pliku i linii w którym został operator użyty.
struct SMemList
{
struct SMemList *next, *prev;
size_t size;
bool array;
char *file;
unsigned int line;
int tag;
};
Poszczególne bloki będą ze sobą połączone wskaźnikami next, prev w postaci listy z jednym elementem korzeniem, do którego
będziemy dodawać nowe elementy listy. Umożliwi nam to późniejsze sprawdzenie, czy wszystkie obszary pamięci zostały zwolnione.
delete,delete[] będzie wiązało się z usunięciem danego bloku powyższej listy. Na koniec wykonywania programu, lista bloków powinna składać się tylko z korzenia. Jeśli będą na niej elementy inne - będą one właśnie takimi wyciekami.
Podczas rezerwowania pamięci dla obiektu OBJ np. tak:
OBJ *ptr = new OBJ();
nasz przeładowany operator new zarezerwuje pamięć rozmiaru
sizeof(SMemList) + sizeof(OBJ);
Na początku zarezerwowanego obszaru znajdzie się blok SMemList, następnie obiekt OBJ. Operator new, new[] zwraca wskaźnik void*, zatem zwrócimy adres nie początku zarezerwowanego wyżej bloku pamięci a początek obszaru dla OBJ:
void * operator new(size_t _size)
{
...
void *ptr = malloc(sizeof(SMemList) + _size);
return ptr + sizeof(SMemList);
}
Podczas zwalniania pamięci operatorami delete, delete[]
będziemy wejściowy wskaźnik przesuwać o rozmiar SMemList i sprawdzać,
czy faktycznie użyliśmy dobrego operatora (delete, delete[]) czy np.
pole tag jest poprawne (ustawiamy je wcześniej dla sprawdzenia poprawności tego kroku),
moglibysmy bowiem zwalniać 'nieswoją' pamięć.
Ostatecznie zwalniamy pamięć całego bloku SMemList i OBJ oraz aktualizujemy listę bloków pamięci usuwając z niej aktualny element.
W bloku SMemList możemy zapisać informacje na temat pliku oraz linii, w której
został użyty operator new, new[] np. za pomocą makro
#define new new(__FILE__, __LINE__)
które automatycznie wstawi nam nazwe pliku, linię wystąpienia instrukcji. I w ten sposób wykorzystamy przeładowane operatory
void* operator new(size_t _size, char *_file, unsigned int _line); void* operator new[](size_t _size, char *_file, unsigned int _line);
1. Do projektu dołączamy plik Debug.cpp, który przeładuje nam operatory, stworzy listę
oraz obsługę. Sprawdzanie listy następuje podczas usunięcia ostatniego statycznego elementu
static _exit_detect _exit_counter;
Jeśli natomiast chcemy również zachować informację o nazwie pliku i linii wystąpienia operatorów,
2. includujemy plik Debug.h
Download: