Утечка памяти C ++ не отображается должным образом

Я использую _CrtDumpMemoryLeaks (); из stdlib.h и crtdbg.h для обнаружения утечек памяти, но я заметил что-то странное в своем коде.

If I do:

int _tmain(int argc, _TCHAR* argv[])
{
    MyClass* myClass = new MyClass();
    _CrtDumpMemoryLeaks(); //I get a memory leak warning
}

Однако, если я это сделаю:

class MyClass
{
    public:
        char* NewChar();
};
char* MyClass::NewChar()
{
    char* test = new char[100];
    return test;
}

MyClass myClass; //Globally declared
int _tmain(int argc, _TCHAR* argv[])
{        
    char* charPointer = myClass.NewChar();
    _CrtDumpMemoryLeaks(); //No warnings
}

Разве я не должен получать предупреждение, так как моя программа завершилась (сразу после _CrtDumpMemoryLeaks ()), и все еще оставался новый символ, который не был удален?

Как я могу обнаружить эти утечки памяти?

Также, используя тот же пример выше, если я добавлю код:

char* anotherPointer = charPointer; //previously filled
delete[] anotherPointer;

Удалит ли это новый символ внутри класса, предотвращая утечки памяти, или мне также следует вызвать delete для charPointer?


person Danicco    schedule 28.01.2013    source источник
comment
Поскольку вы удаляете вещи, а не указатели, не имеет значения, какой указатель указывает на что-то, когда вы его удаляете. Другими словами, удаление с помощью anotherPointer - то же самое, что удаление с помощью charPointer, и вы должны выполнить только одно из них.   -  person molbdnilo    schedule 28.01.2013
comment
В ПОРЯДКЕ. просто весь вопрос изменился или это был я?   -  person WhozCraig    schedule 28.01.2013


Ответы (4)


Если вы хотите поймать (или иметь хороший шанс уловить) утечку глобальных объектов, попробуйте установить флаг отладки CRT _CRTDBG_LEAK_CHECK_DF в начале main. Флаг вызывает дамп обнаруженных утечек после глобальных деструкторов.

Чтобы ответить на ваши вопросы:

Разве я не должен получать предупреждение, так как моя программа вышла (сразу после _CrtDumpMemoryLeaks ()), и все еще оставался новый символ, который не был удален?

Дайте определение «предупреждение». Вызов _CrtDumpMemoryLeaks() должен сбрасывать все, что в данный момент не выполнено. Ваша программа, выходящая после этого, не будет делать другой дамп, если это не настроено.

Как я могу обнаружить эти утечки памяти?

Они должны быть правильно обнаружены при условии, что вы используете отладочную CRT и правильно настроили _Crt конфигурацию, которая в большинстве случаев есть по умолчанию.


Следующий код настраивает _Crt систему дампа для создания дампа всех объектов по запросу и прямо перед окончательным выходом (после завершения main() и уничтожения глобальной статики).

class MyLeak
{
public:
    MyLeak() { new unsigned char[1024]; }

    char * NewChar() { return new char[1024]; }
};

MyLeak myLeak;
int main(int argc, char *argv[])
{
    int tmpFlag = _CrtSetDbgFlag( _CRTDBG_REPORT_FLAG );
    tmpFlag |= _CRTDBG_LEAK_CHECK_DF;
    _CrtSetDbgFlag(tmpFlag);

    _CrtMemState ms = {0};
    _CrtMemCheckpoint(&ms);

    char *ptr = myLeak.NewChar();
    _CrtMemDumpAllObjectsSince(&ms);

    OutputDebugString("Exiting main()\n");
    return EXIT_SUCCESS;
}

Журнал вывода отладки

Dumping objects ->
{69} normal block at 0x000000000048B800, 1024 bytes long.
 Data: <                > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD 
Object dump complete.
Exiting main()
Detected memory leaks!
Dumping objects ->
{69} normal block at 0x000000000048B800, 1024 bytes long.
 Data: <                > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD 
{68} normal block at 0x000000000048B390, 1024 bytes long.
 Data: <                > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD 
Object dump complete.

Обратите внимание, что в первом дампе записывается только внутреннее распределение, поскольку я использовал контрольную точку, а затем вызов dump-Since. Второй дамп (вызываемый после завершения main()) по-прежнему записывает их оба, поскольку они оба еще не обработаны.

То, что у вас происходит утечка дампа после вашего динамического распределения (которое предоставлено объектом в глобальном пространстве памяти, но это не имеет ничего общего с распределением, это просто код, выделяющий память и возвращающий ее вам), не выглядит правильным. Вы должны получать дамп объекта всех невыполненных распределений CRT при любом _CrtDumpMemoryLeaks() вызове с начала программы.

person WhozCraig    schedule 28.01.2013

Подумай об этом. Глобальная переменная myClass имеет статический срок хранения. Он будет уничтожен после закрывающей фигурной скобки main при завершении программы. _CrtDumpMemoryLeaks () не может утвердительно обнаружить утечку, поскольку деструктор MyClass еще (правильно) не был вызван.

person Chubsdad    schedule 28.01.2013

Разве я не должен получать предупреждение после выхода из моей программы (сразу после_CrtDumpMemoryLeaks ())

Да, вы должны получить предупреждение, но не из-за выхода, ...

и остался еще не удаленный новый символ?

... но из-за этого, т.е. из-за того, что все еще оставался динамически выделенный массив char, который не был удален.

Как я могу обнаружить эти утечки памяти?

Вы уверены, что скомпилировали программу с _DEBUG #defined? Если вы это сделали, это должно быть обнаружено. (Не могу проверить себя прямо сейчас и не использовал это целую вечность, поэтому я не могу ручаться за это.)

Также, используя тот же пример выше, если я добавлю код:

char* anotherPointer = charPointer; //previously filled delete [] anotherPointer; `

Удалит ли это новый символ внутри класса, предотвращая утечки памяти, или мне также следует вызвать delete для charPointer?

Это удалит динамически распределенный массив символов. После этого charPointer находится висячий указатель, который больше нельзя использовать - ни разыменовать, ни удалить [] 'd. (То же самое относится и к anotherPointer.) Повторное удаление удаленного массива с помощью того же указателя или другой переменной недопустимо и вызывает Неопределенное поведение.

person JoergB    schedule 28.01.2013

Вы подключаетесь к библиотекам отладки? В противном случае вы не получите сообщения о неосвобожденных блоках памяти. Подсказка: используйте параметр / MDd.

Обратите внимание, что вопреки тому, что предлагает название функции, все, что она делает, это распечатывает список блоков памяти, которые вы еще не освободили. Вызывая его в конце main (), вы заставляете его сообщать о блоках, которые вы явно выделили, но не освободили явно. Поэтому, если у вас есть одноэлементные объекты, выделяемые динамически, они будут зарегистрированы как утечка, тогда как вы вполне законно можете освободить их с помощью функции, подключенной через механизм _atexit ().

person Hari Mahadevan    schedule 28.01.2013