Я записал следующий отладчик "кучи" для демонстрации утечек памяти, дважды удаляет, и неправильные формы удаляет (т.е. пытающийся удалить массив с delete p
вместо delete[]
p) начинающим программистам.
Я хотел бы получить некоторую обратную связь на этом от сильных программистов на C++, потому что я никогда не делал этого прежде, и я уверен, что сделал некоторые глупые ошибки.Спасибо!
#include <cstdlib>
#include <iostream>
#include <new>
namespace
{
const int ALIGNMENT = 16;
const char* const ERR = "*** ERROR: ";
int counter = 0;
struct heap_debugger
{
heap_debugger()
{
std::cerr << "*** heap debugger started\n";
}
~heap_debugger()
{
std::cerr << "*** heap debugger shutting down\n";
if (counter > 0)
{
std::cerr << ERR << "failed to release memory " << counter << " times\n";
}
else if (counter < 0)
{
std::cerr << ERR << (-counter) << " double deletes detected\n";
}
}
} instance;
void* allocate(size_t size, const char* kind_of_memory, size_t token) throw (std::bad_alloc)
{
void* raw = malloc(size + ALIGNMENT);
if (raw == 0) throw std::bad_alloc();
*static_cast<size_t*>(raw) = token;
void* payload = static_cast<char*>(raw) + ALIGNMENT;
++counter;
std::cerr << "*** allocated " << kind_of_memory << " at " << payload << " (" << size << " bytes)\n";
return payload;
}
void release(void* payload, const char* kind_of_memory, size_t correct_token, size_t wrong_token) throw ()
{
if (payload == 0) return;
std::cerr << "*** releasing " << kind_of_memory << " at " << payload << '\n';
--counter;
void* raw = static_cast<char*>(payload) - ALIGNMENT;
size_t* token = static_cast<size_t*>(raw);
if (*token == correct_token)
{
*token = 0xDEADBEEF;
free(raw);
}
else if (*token == wrong_token)
{
*token = 0x177E6A7;
std::cerr << ERR << "wrong form of delete\n";
}
else
{
std::cerr << ERR << "double delete\n";
}
}
}
void* operator new(size_t size) throw (std::bad_alloc)
{
return allocate(size, "non-array memory", 0x5AFE6A8D);
}
void* operator new[](size_t size) throw (std::bad_alloc)
{
return allocate(size, " array memory", 0x5AFE6A8E);
}
void operator delete(void* payload) throw ()
{
release(payload, "non-array memory", 0x5AFE6A8D, 0x5AFE6A8E);
}
void operator delete[](void* payload) throw ()
{
release(payload, " array memory", 0x5AFE6A8E, 0x5AFE6A8D);
}
Вместо того, чтобы вести навязчивые записи, вы можете вести список всех выделенных ресурсов. Затем вы можете освободить память, не уничтожая свои собственные данные, и отслеживать, сколько раз конкретный адрес «удалялся», а также находить места, где программа пытается удалить несовпадающий адрес (то есть не в списке).
void* raw = static_cast<char*>(payload) - ALIGNMENT;
Если полезная нагрузка
уже удалена, не будет ли это неопределенным поведением?
Объясните, почему вы выбрали "ALIGNMENT" в качестве идентификатора. Объясните, почему вы выбрали 16. Аргументируйте, как ваш алгоритм отлавливает наиболее распространенные ошибки, такие как переполнение конца блока, выделенного из кучи, или забывание освободить память.
Это действительно отличное начало. Вот мои 2 цента, как вы просили оставить отзыв:
Я не очень хорошо слежу за тем, как вы используете жестко запрограммированные константы / константные строки - поместить их в перечисление? И я действительно не очень хорошо понимаю токен. Требуется больше комментариев