Открыто
06.04.2015, 08:45
Реализовать обнаружение утечек памяти с C++ программах.
Простейший способ обнаружения утечки памяти с помощью функции _DumpMemoryLeaks в Visual Studio.
// main.cpp
void main(void) {
    // код
    _CrtDumpMemoryLeaks(); // здесь вся выделенная память должна быть освобождена
}
Но данный способ не работает, если существуют сложные глобальные и статические объекты.
// main.cpp
class A {
    char * p;
public:
    A(void) : p(new char[1024]) {}
    ~A(void) { delete[] p; }
};
static A staticA;
void fn(void) {
    static A localstaticA;
}
void main(void) {
    fn();
    _CrtDumpMemoryLeaks(); // глобальные объекты не уничтожены, их деструкторы не вызваны
}
Кроме того, глобальные статические объекты могут быть объявлены и созданы в другом модуле.
// another.cpp
class B {
    char * p;
public:
    B(void) : p(new char[256]) {}
    ~B(void) { delete[] p; }
};
static B staticB;
void g(void) {
    static B localstaticB;
}
Первое приближение
Чтобы корректно детектировать утечки памяти, необходимо создать глобальный объект, который в деструкторе будет вызывать _CrtDumpMemoryLeaks().
class MemoryDump {
public:
    MemoryDump(void) { }
    ~MemoryDump(void) { _CrtDumpMemoryLeaks(); }
};
Объект класса MemoryDump должен быть создан в самом начале cpp файла, так чтобы он, среди других глобальных и статических объектов всегда создавался первым. Тогда при деинициализации гарантируется обратный порядок вызова деструкторов, и поэтому _CrtDumpMemoryLeaks() в деструкторе будет вызвана уже после вызова деструкторов у всех остальных глобальных объектов.
Второе приближение
Если в проекте присутствует несколько cpp файлов, то потребуется создать несколько таких глобальных объектов (т.к. порядок инициализации / деинициализации глобальных объектов в разных модулях не определён и может быть любым). Но при этом требуется чтобы _CrtDumpMemoryLeaks() вызывалась только в последнем вызове деструктора.
Для этого потребуется преобразовать наш класс в соответствии с паттерном одиночка: создание и уничтожение объекта происходит множество раз, но реально существует всегда только один экземпляр объекта, который один раз создаётся (первый вызов конструктора) и только один раз уничтожается (последний вызов деструктора).
Результат
Необходимо оформить класс и всю необходимую логику в виде отдельного модуля, а также сформулировать дополнительные правила использования этого модуля (если требуется).
Приведённый выше код можно использовать в качестве тестовых примеров для проверки работы модуля.