Как защитить от утечек памяти?

Недавно я беседовал о позиции С++, и меня спросили, как я защищаюсь от создания утечек памяти. Я знаю, что не дал удовлетворительного ответа на этот вопрос, поэтому я бросаю его вам. Каковы наилучшие способы защиты от утечек памяти?

Спасибо!

Ответ 1

Все ответы, приведенные до сих пор, сводятся к следующему: избегайте вызова delete.

Каждый раз, когда программист должен вызывать delete, у вас есть потенциальная утечка памяти. Вместо этого вызовите вызов delete автоматически. С++ гарантирует, что локальные объекты будут вызваны деструкторами, когда они выйдут из области видимости. Используйте эту гарантию, чтобы обеспечить автоматическое удаление распределений памяти.

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

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

Конечно, в зависимости от использования может потребоваться другая семантика. Вам просто нужен простой случай, когда распределение должно длиться ровно столько, сколько класс обертки живет? Затем используйте boost::scoped_ptr или, если вы не можете использовать boost, std::auto_ptr. У вас есть неизвестное количество объектов, ссылающихся на распределение, не зная, как долго каждый из них будет жить? Тогда опорный отсчет boost::shared_ptr является хорошим решением.

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

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

Но все, что у всех есть, есть ответ на ваш вопрос: Иерархия RAII: Инициализация ресурсов. Распределение памяти - это своего рода ресурс. Ресурсы должны быть получены, когда объект инициализируется и освобождается объектом itslef, когда он уничтожен.

Сделайте для вас CMS-область и правила жизни. Никогда не вызывайте delete вне объекта RAII, будь то класс контейнера, интеллектуальный указатель или какая-либо специальная оболочка для одного выделения. Пусть объект обрабатывает назначенный ему ресурс.

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

Ответ 2

  • Не выделяйте память в куче, если вам это не нужно. Большая часть работы может быть выполнена в стеке, поэтому вам нужно делать только выделение памяти кучи, когда вам абсолютно необходимо.

  • Если вам нужен выделенный кучей объект, принадлежащий одному другому объекту, используйте std::auto_ptr.

  • Используйте стандартные контейнеры или контейнеры из Boost вместо того, чтобы изобретать свои собственные.

  • Если у вас есть объект, на который ссылаются несколько других объектов и не принадлежит ни одному, в частности, используйте либо std::tr1::shared_ptr, либо std::tr1::weak_ptr - в зависимости от вашего варианта использования.

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

Ответ 3

Вы бы хорошо прочитали RAII.

Ответ 4

заменить новый на shared_ptr. В основном RAII. сделать исключение кода безопасным. Использование stl везде возможно. Если вы используете указатели подсчета ссылок, убедитесь, что они не образуют циклы. SCOPED_EXIT от boost также очень полезен.

Ответ 5

  • (Легко) Никогда не позволяйте сырому указателю иметь собственный объект (найдите свой код для регулярного выражения "\= *new". Вместо этого используйте shared_ptr или scoped_ptr, или даже лучше, вместо этого используйте реальные переменные указателей так часто, как вы можете.

  • (Жесткий) Убедитесь, что у вас нет круговых ссылок, а shared_ptrs указывают друг на друга, используйте weak_ptr, чтобы их разбить.

Готово!

Ответ 6

Используйте все виды интеллектуальных указателей.

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

Ответ 7

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

Ответ 8

В дополнение к советам по RAII, не забудьте сделать виртуальный виртуальный денструктор вашего базового класса виртуальным.

Ответ 9

Чтобы избежать утечек памяти, вы должны сделать четкое и четкое представление о том, кто несет ответственность за удаление любого динамически выделенного объекта.

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

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

В некоторых ситуациях, включая недисциплинированных программистов и сложных структур данных, вам, возможно, придется прибегнуть к более продвинутым методам, таким как подсчет ссылок. Каждому объекту присваивается "счетчик", который представляет собой число других переменных, которые указывают на него. Всякий раз, когда часть кода решает перестать указывать на объект, счетчик уменьшается. Когда счетчик достигает нуля, объект удаляется. Для подсчета ссылок требуется строгая обработка счетчика. Это можно сделать с помощью так называемых "умных указателей": это объекты, которые являются функциональными указателями, но которые автоматически настраивают счетчик на их собственное создание и разрушение.

Счет ссылок работает довольно хорошо во многих ситуациях, но они не могут обрабатывать циклические структуры. Поэтому для самых сложных ситуаций вам нужно прибегнуть к тяжелой артиллерии, т.е. Сборщик мусора . Я ссылаюсь на GC для C и С++, написанный Хансом Бёмом, и он использовался в некоторых довольно крупных проектах (например, Inkscape). Точка сборщика мусора должна поддерживать глобальное представление о полном объеме памяти, чтобы узнать, используется ли данный экземпляр или нет. Это правильный инструмент, когда инструментов локального просмотра, таких как подсчет ссылок, недостаточно. Можно утверждать, что в этот момент следует спросить себя, является ли С++ правильным языком для данной проблемы. Сбор мусора лучше всего работает, когда язык является кооперативным (это разблокирует множество оптимизаций, которые не выполняются, когда компилятор не знает, что происходит с памятью, как типичный компилятор C или С++).

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

Ответ 11

Очень хороший способ - использовать Smart Pointers, boost/tr1:: shared_ptr. Память будет бесплатной, как только выделенная (стек выделенная) интеллектуальная указатель выходит за пределы области видимости.

Ответ 12

  • Умные указатели.
  • Управление памятью.
  • Заменить "новый" и "удалить" или использовать собственные макросы/шаблоны.

Ответ 13

На x86 вы можете регулярно использовать Valgrind для проверки кода

Ответ 14

Вы можете использовать эту утилиту. Если вы работаете в Linux, используйте valgrid (он бесплатный). Используйте Deleaker в Windows.