Yo le recomendaría que use valgrind
para Linux. Capturará memoria no liberada, entre otros errores, como escribir en la memoria no asignada. Otra opción es mudflap, que también le informa sobre la memoria no liberada. Use las opciones -fmudflap -lmudflap
con gcc, luego inicie su programa con MUDFLAP_OPTIONS=-print-leaks ./my_program
.
Aquí hay un código muy simple. No es adecuado para el seguimiento sofisticado, pero pretende mostrarle cómo lo haría en principio, si tuviera que implementarlo usted mismo. Algo como esto (cosas omitidas que llaman al new_handler registrado y otros detalles).
template<typename T>
struct track_alloc : std::allocator<T> {
typedef typename std::allocator<T>::pointer pointer;
typedef typename std::allocator<T>::size_type size_type;
template<typename U>
struct rebind {
typedef track_alloc<U> other;
};
track_alloc() {}
template<typename U>
track_alloc(track_alloc<U> const& u)
:std::allocator<T>(u) {}
pointer allocate(size_type size,
std::allocator<void>::const_pointer = 0) {
void * p = std::malloc(size * sizeof(T));
if(p == 0) {
throw std::bad_alloc();
}
return static_cast<pointer>(p);
}
void deallocate(pointer p, size_type) {
std::free(p);
}
};
typedef std::map< void*, std::size_t, std::less<void*>,
track_alloc< std::pair<void* const, std::size_t> > > track_type;
struct track_printer {
track_type * track;
track_printer(track_type * track):track(track) {}
~track_printer() {
track_type::const_iterator it = track->begin();
while(it != track->end()) {
std::cerr << "TRACK: leaked at " << it->first << ", "
<< it->second << " bytes\n";
++it;
}
}
};
track_type * get_map() {
// don't use normal new to avoid infinite recursion.
static track_type * track = new (std::malloc(sizeof *track))
track_type;
static track_printer printer(track);
return track;
}
void * operator new(std::size_t size) throw(std::bad_alloc) {
// we are required to return non-null
void * mem = std::malloc(size == 0 ? 1 : size);
if(mem == 0) {
throw std::bad_alloc();
}
(*get_map())[mem] = size;
return mem;
}
void operator delete(void * mem) throw() {
if(get_map()->erase(mem) == 0) {
// this indicates a serious bug
std::cerr << "bug: memory at "
<< mem << " wasn't allocated by us\n";
}
std::free(mem);
}
int main() {
std::string *s = new std::string;
// will print something like: TRACK: leaked at 0x9564008, 4 bytes
}
tenemos que usar nuestro propio asignador de nuestro mapa, debido a la estándar utilizará nuestro operador reemplazado nueva, lo que resultaría en un bucle infinito.
Asegúrese de que si anula el operador nuevo, use el mapa para registrar sus asignaciones. Eliminar la memoria asignada por las formas de ubicación de nuevo usará ese operador de eliminación también, por lo que puede ser complicado si algún código que no conoce tiene operador sobrecargado nuevo que no usa su mapa, porque la eliminación del operador le dirá que no fue asignado y use std::free
para liberar la memoria.
También tenga en cuenta, como Pax señaló también para su solución, esto solo mostrará las fugas causadas por el código utilizando nuestro propio operador definido new/delete. Por lo tanto, si desea usarlos, coloque su declaración en un encabezado e inclúyala en todos los archivos que deben ser vistos.
No hay una respuesta genérica disponible. Proporcione más información sobre el sistema operativo y la plataforma utilizada. – kauppi
Necesitaría una solución que funcione al menos en Linux y Windows, y preferiblemente también en Mac OS. – Anteru