Чтобы избежать утечек памяти, вам необходимо иметь четкое и определенное представление о том, кто отвечает за удаление любого динамически размещаемого объекта.
C ++ позволяет создавать объекты в стеке (то есть в виде локальных переменных). Это связывает создание и уничтожение потока управления: объект создается, когда выполнение программы достигает своего объявления, и объект уничтожается, когда выполнение выходит из блока, в котором было сделано это объявление. Когда распределение соответствует этому шаблону, используйте его. Это избавит вас от многих неприятностей.
Для других случаев, если вы можете определить и документ с четким понятием ответственности, тогда это может работать нормально. Например, у вас есть метод или функция, которая возвращает указатель на вновь выделенный объект, и вы документально подтверждаете, что вызывающая сторона становится ответственной за окончательное удаление этого экземпляра. Четкая документация в сочетании с хорошей дисциплиной программиста (что нелегко достичь!) Могут решить многие оставшиеся проблемы управления памятью.
В некоторых ситуациях, включая недисциплинированных программистов и сложные структуры данных, вам, возможно, придется прибегнуть к более продвинутым методам, таким как подсчет ссылок. Каждому объекту присваивается «счетчик», который представляет собой число других переменных, которые указывают на него. Всякий раз, когда часть кода решает больше не указывать на объект, счетчик уменьшается. Когда счетчик достигает нуля, объект удаляется. Подсчет ссылок требует строгой обработки счетчиков. Это можно сделать с помощью так называемых «умных указателей»: это объекты, которые являются функционально указателями, но автоматически настраивают счетчик на свое собственное создание и уничтожение.
Подсчет ссылок работает довольно хорошо во многих ситуациях, но они не могут обрабатывать циклические структуры. Поэтому для самых сложных ситуаций вам придется прибегнуть к тяжелой артиллерии, то есть к сборщику мусора . Я ссылаюсь на GC для C и C ++, написанный Хансом Бёмом, и он использовался в некоторых довольно крупных проектах (например, Inkscape ). Задача сборщика мусора состоит в том, чтобы поддерживать глобальный взгляд на все пространство памяти, чтобы знать, используется ли данный экземпляр по-прежнему или нет. Это правильный инструмент, когда инструментов локального просмотра, таких как подсчет ссылок, недостаточно. Можно утверждать, что в этот момент следует спросить себя, является ли C ++ правильным языком для рассматриваемой проблемы. Сборка мусора работает лучше всего, когда язык совместный (это открывает множество оптимизаций, которые невозможно выполнить, когда компилятор не знает, что происходит с памятью, как типичный компилятор C или C ++).
Обратите внимание, что ни один из методов, описанных выше, не позволяет программисту перестать думать. Даже GC может страдать от утечек памяти, потому что он использует достижимость как приближение будущее использование (есть теоретические причины, которые подразумевают, что, в общем смысле, невозможно обнаружить все объекты, которые впоследствии не будут использоваться). Возможно, вам все равно придется установить для некоторых полей значение NULL
, чтобы сообщить GC, что вы больше не будете обращаться к объекту через данную переменную.