Утечки памяти в C ++ (через new + delete) - PullRequest
6 голосов
/ 31 августа 2009

Чтобы приложение не имело утечек памяти, совпадает ли число новых в проекте C ++ с числом удалений?

Ответы [ 10 ]

19 голосов
/ 31 августа 2009

Если вы имеете в виду, нужно ли вам в исходном коде столько же экземпляров delete, сколько экземпляров new, тогда нет. Вы можете иметь объекты new в нескольких местах, но все эти объекты delete d должны быть в одной строке кода. На самом деле это обычная идиома.

Интеллектуальные указатели разных типов, как правило, принимают много разных объектов new, редактируемых во многих местах кода пользователя, и delete их из одного места в коде библиотеки.

Редактировать

Технически, каждый успешный вызов выделения памяти должен быть сопоставлен с вызовом делокации, который берет возвращенный указатель из исходного вызова выделения.

Большинство new выражений приводят к вызову operator new, который выделяет память и создает объект во вновь выделенной памяти. Использование выражения delete уничтожает объект и вызывает вызов operator delete, который должен освободить выделенную память.

Появились новые выражения, которые конструируют объекты в заранее выделенной памяти (размещение new). Эти не должны совпадать с выражением удаления, но может потребоваться освободить предварительно выделенную память таким образом, чтобы это соответствовало исходному выделению.

17 голосов
/ 31 августа 2009

Если вы имели в виду «в исходном коде», то нет.

См. Этот код:

int main()
{
    char* buffer = 0; 


    for( int i = 0; i < 42; ++i )
    {
        buffer = new char[1024];
    }

    delete [] buffer; 

    return 0;
}

1 новый, 1 удаление, ((42 - 1) * 1024) байтов утечки памяти.

Если вы имели в виду «новый и удалить вызов во время выполнения», тогда да. Каждая память, полученная с новым, должна быть освобождена с помощью delete:

int main()
{
    std::vector<char*> bufferList; // or nullptr or NULL whatever


    for( int i = 0; i < 42; ++i )
    {
        bufferList.push_back( new char[1024] );
    }

    for( int i = 0; i < bufferList.size(); ++i )
    {
        delete [] bufferList[i]; 
    }

    return 0;
}

Теперь при выполнении мы получаем удаление, выполненное для каждого нового выполненного <=> без утечек.

7 голосов
/ 31 августа 2009

Да. Каждое новое должно соответствовать удалению.

Однако удаление часто скрыто от вас, например:

{
  std::auto_ptr p( new Foo );
}

Здесь есть новое, но удаление (которое происходит автоматически в конце блока) скрыт в реализация std :: auto_ptr.

2 голосов
/ 31 августа 2009

Существуют сложные инструменты, такие как Rational Purify, для проверки утечек памяти в программах на C ++. К сожалению, в общем, очень нетривиальная проблема проверить, что сам код свободен от утечек памяти перед выполнением. Поэтому будьте проще, следуйте рекомендациям и тестируйте как можно больше во время выполнения.

1 голос
/ 31 августа 2009

Вы спрашиваете о счетчике вызовов во время выполнения (например, подсчитанном с помощью профилировщика инструментов)? Наличие одинакового количества вызовов к операторам new (исключая размещение новых) и delete не является ни обязательным, ни достаточным условием кода без утечек:

  • Удаление NULL безвредно, поэтому многие программы без утечек вызывают delete больше раз, чем new.
  • Программа, которая вызывает delete столько раз, сколько new, но иногда удаляет NULL, имеет утечку. Так что некоторые программы вызывают delete чаще, чем new, но иногда удаляют NULL.
  • Программа, которая вызывает new чаще, чем delete, имеет утечку.

Чтобы убедиться в отсутствии утечек, вы должны убедиться, что каждый адрес, возвращаемый с new, передан в delete, а не просто проверить, что количество вызовов совпадает. И даже это упрощает, поскольку адреса многократно используются для многократного распределения.

Кроме того, программы, которые не пропускают выделенную им память, могут по-прежнему пропускать другие ресурсы (например, дескрипторы файлов ОС).

1 голос
/ 31 августа 2009

Вам необходимо сопоставить вызов с новым вызовом для удаления. Поскольку C ++ является объектно-ориентированным языком программирования, рассмотрите возможность использования класса для создания (в конструкторе) и удаления (в деструкторе) переменных, объявленных или используемых в классе. Фактически, это было бы преимуществом Resource Acquisition * Initialization или RAII (для краткости). Если вам не хочется программировать это самостоятельно, вы всегда можете использовать память из STL .

Одно важное замечание: если вы выделяете переменную с новым и ваш код может выдать исключение, вы можете потерять память, если это исключение не будет перехвачено и переменная удалена соответствующим образом.

0 голосов
/ 31 августа 2009

Если вам нужен предварительный способ обнаружения утечек памяти, подсчет новых / удаленных файлов не поможет вообще. Однако, если вы используете MS VC, вы можете использовать CRT для выполнения очень простого, но полезного обнаружения утечек:

_CrtSetReportFile( _CRT_WARN, _CRTDBG_FILE_STDOUT);
_CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );

Это заставит CRT вызывать _CrtDumpMemoryLeaks непосредственно перед выгрузкой CRT. Если они есть, они выведут их на консоль:

Detected memory leaks!
Dumping objects ->
{342} normal block at 0x05A28FD8, 4 bytes long.
 Data: <    > 00 00 00 00 
Object dump complete.

Существует также методика определения точного места утечки по номеру блока ({342}), но иногда достаточно просто узнать, есть ли утечки.

Он не заменяет необходимость в надлежащих средствах обнаружения утечек памяти, но может ограничивать потребности в них.

Я всегда использую эту технику в юнит-тестах. Позволяет мне обнаруживать очень глупые утечки памяти очень рано.

0 голосов
/ 31 августа 2009

Чтобы избежать утечек памяти, ваше приложение должно освободить всю память, которую оно больше не использует , если не выделит ее во время завершения. Нет понятия утечки памяти в программе без цикла событий (потому что, в общем, их время жизни заканчивается с самого начала :-)).

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

0 голосов
/ 31 августа 2009

Не совсем. Любой объект, выделенный с помощью оператора new, должен быть освобожден с помощью оператора delete. Хорошо иметь несколько операторов (новых или удаленных) в разных ветках.

Лично я использую Boost's shared_ptr при написании кода на C ++.

0 голосов
/ 31 августа 2009

Ну, может, если у вас точно нет news и нет deletes, или если у вас есть другие классы, например stl, для управления памятью.

Вам необходимо сопоставить количество вызовов new с количеством вызовов delete.

Чаще всего это усложняется.

...