Общие руководящие принципы для предотвращения утечек памяти в [закрытом] C++

Указатель NULL - это тот, который указывает на никуда. Когда вы разыскиваете указатель p, вы говорите «дайте мне данные в месте, хранящемся в« p ». Когда p является нулевым указателем, местоположение, хранящееся в p, является nowhere, вы говорите «Дайте мне данные в месте« нигде ». Очевидно, он не может этого сделать, поэтому он выбрасывает NULL pointer exception.

В общем, это потому, что что-то не было правильно инициализировано.

127
задан 5 revs, 4 users 67%dulipishi 27 September 2016 в 14:57
поделиться

29 ответов

Вместо руководящей памяти вручную, попытайтесь использовать интеллектуальные указатели когда это применимо.
Смотрят на lib Повышения , TR1, и интеллектуальные указатели .
Также интеллектуальные указатели являются теперь частью стандарта C++, названного C++ 11 .

39
ответ дан 24 November 2019 в 00:40
поделиться

Точно один возврат из любой функции. Тем путем можно сделать освобождение там и никогда не пропускать его.

слишком легко сделать ошибку иначе:

new a()
if (Bad()) {delete a; return;}
new b()
if (Bad()) {delete a; delete b; return;}
... // etc.
-3
ответ дан 24 November 2019 в 00:40
поделиться

C++ разработан RAII в памяти. Нет действительно никакого лучшего способа управлять памятью в C++, я думаю. Но бойтесь выделять очень большие блоки (как буферные объекты) на локальном объеме. Это может вызвать переполнения стека и, если существует дефект в границах, проверяющих при использовании того блока, можно перезаписать другие переменные или обратные адреса, который приводит ко всем дырам в системе безопасности видов.

0
ответ дан 24 November 2019 в 00:40
поделиться

Управляйте памятью тем же путем, Вы управляете другими ресурсами (дескрипторы, файлы, соединения дб, сокеты...). GC не помог бы Вам с ними также.

0
ответ дан 24 November 2019 в 00:40
поделиться

Одним из единственных примеров о выделении и уничтожении в различных местах является создание потока (параметр, который Вы передаете). Но даже в этом случае легко. Вот функция/метод, создающая поток:

struct myparams {
int x;
std::vector<double> z;
}

std::auto_ptr<myparams> param(new myparams(x, ...));
// Release the ownership in case thread creation is successfull
if (0 == pthread_create(&th, NULL, th_func, param.get()) param.release();
...

Здесь вместо этого функция потока

extern "C" void* th_func(void* p) {
   try {
       std::auto_ptr<myparams> param((myparams*)p);
       ...
   } catch(...) {
   }
   return 0;
}

Симпатичный easyn не так ли? В случае, если создание потока перестало работать, ресурс будет free'd (удаленным) auto_ptr, иначе владение будет передано потоку. Что, если поток так быстр, что после создания он выпускает ресурс перед

param.release();

, называют в основной функции/методе?Ничего! Поскольку мы 'скажем' auto_ptr игнорировать освобождение. Действительно ли управление памятью C++ легко не так ли? С наилучшими пожеланиями,

Ema!

0
ответ дан 24 November 2019 в 00:40
поделиться

Мы переносим все наши функции выделения со слоем, который добавляет краткую строку в передней стороне и флаг сигнальной метки в конце. Так, например, у Вас был бы вызов к "myalloc (pszSomeString, iSize, iAlignment); или новый ("описание", iSize) MyObject (); который внутренне выделяет указанный размер плюс достаточно пространства для Вашего заголовка и сигнальной метки. Конечно, не забывайте комментировать это для сборок неотладки! Требуется немного больше памяти, чтобы сделать это, но преимущества явно перевешивают затраты.

Это обладает тремя преимуществами - сначала это позволяет Вам легко, и быстро отследите то, что код пропускает путем выполнения быстрых поисков кода, выделенного в определенных 'зонах', но не очищенный, когда те зоны должны были освободить. Может также быть полезно обнаружить, когда граница была перезаписана путем проверки, чтобы гарантировать, что все сигнальные метки неповреждены. Это сохранило нас многочисленные времена при попытке найти, что те хорошо скрытые отказывают или оплошности массива. Третье преимущество находится в отслеживании использования памяти для наблюдения, кто крупные игроки - сопоставление определенных описаний в MemDump говорит Вам, когда 'звук' поднимает путь больше пространства, чем Вы ожидали, например.

0
ответ дан 24 November 2019 в 00:40
поделиться

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

Это может также быть сделано во время компиляции путем замены новых операторов и удалить и другие функции выделения памяти.

, Например, регистрация этого сайт [Отладка выделения памяти в C++] Примечание: существует прием для оператора delete также что-то вроде этого:

#define DEBUG_DELETE PrepareDelete(__LINE__,__FILE__); delete
#define delete DEBUG_DELETE

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

Вы могли также попробовать что-то как BoundsChecker в соответствии с Visual Studio, которая довольно интересна и проста в использовании.

0
ответ дан 24 November 2019 в 00:40
поделиться

valgrind (только польза для *отклоняет платформы), очень хорошее средство проверки памяти

1
ответ дан 24 November 2019 в 00:40
поделиться

Если Вы собираетесь управлять своей памятью вручную, у Вас есть два случая:

  1. я создал объект (возможно, косвенно, путем вызывания функции, которая выделяет новый объект), я использую его (или функция, я называю использование им), затем я освобождаю его.
  2. Кто-то дал мне ссылку, таким образом, я не должен освобождать ее.

, Если необходимо нарушить какое-либо из этих правил, зарегистрируйте его.

Это - все о владении указателя.

1
ответ дан 24 November 2019 в 00:40
поделиться
  • Стараются не выделять объекты динамично. Пока классы имеют соответствующих конструкторов и деструкторы, используют переменную типа класса, не указатель на нее, и Вы избегаете динамического выделения и освобождения, потому что компилятор сделает это для Вас.
    На самом деле это - также механизм, используемый "интеллектуальными указателями" и называемый RAII некоторыми из других устройств записи ;-).
  • при передаче объектов другим функциям предпочтите параметры ссылки по указателям. Это избегает некоторых возможных ошибок.
  • Объявляют константу параметров, если это возможно, особенно указатели на объекты. Тем путем объекты не могут быть освобождены "accidentially" (кроме того, если Вы выбрасываете константу ;-))).
  • Минимизируют количество мест в программе, где Вы делаете выделение памяти и освобождение. Например, если Вы действительно выделяете или несколько раз освобождаете тот же тип, запишите функцию для него (или метод фабрики ;-)).
    Этот способ, которым можно создать вывод отладки (какие адреса выделены и освобождены...), легко, при необходимости.
  • Использование фабрика функционируют для выделения объектов нескольких связанных классов от единственной функции.
  • , Если Ваши классы имеют общий базовый класс с виртуальным деструктором, можно освободить всех их использующих ту же функцию (или статический метод).
  • Проверка, которую любит Ваша программа с инструментами, очищает (к сожалению, много $/в‚ ¬/...).
1
ответ дан 24 November 2019 в 00:40
поделиться

Для MSVC только, добавьте следующее к вершине каждого .cpp файла:

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

Затем при отладке с VS2003 или больше, Вам скажут о любых утечках, когда Ваша программа выйдет (это отслеживает новый/удаляющий). Это является основным, но это помогло мне в прошлом.

1
ответ дан 24 November 2019 в 00:40
поделиться

Другие упомянули способы избежать утечек памяти во-первых (как интеллектуальные указатели). Но инструмент профилирования и анализа памяти является часто единственным способом разыскать проблемы памяти, после того как у Вас есть они.

Valgrind memcheck является превосходным свободным.

1
ответ дан 24 November 2019 в 00:40
поделиться

Если Вы можете, использовать повышение shared_ptr и стандартный C++ auto_ptr. Они передают семантику владения.

при возврате auto_ptr Вы говорите вызывающей стороне, что даете им владение памяти.

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

Они семантика также относятся к параметрам. Если вызывающая сторона передает Вас auto_ptr, они дают Вам владение.

1
ответ дан 24 November 2019 в 00:40
поделиться

Частый источник этих ошибок - когда у Вас есть метод, который принимает ссылку или указатель на объект, но оставляет владение неясным. Разработайте и комментарий, что конвенции могут сделать это менее вероятно.

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

Используя константу может помочь много в некоторых случаях. Если функция не изменит объект и не хранит ссылку на него, которая сохраняется после того, как он возвращается, примите ссылку константы. От чтения кода вызывающей стороны будет очевидно, что Ваша функция не приняла владение объекта. У Вас, возможно, была та же функция, принимают указатель неконстанты, и вызывающая сторона может или не могла предположить, что вызываемый принял владение, но со ссылкой константы нет никакого вопроса.

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

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

2
ответ дан 24 November 2019 в 00:40
поделиться

Подсказки в порядке Важности:

-Tip#1 Всегда не забывают объявлять Ваши "виртуальные" деструкторы.

smartpointers

-Tip#4 повышения Использования RAII

-Tip#3 Использования-Tip#2 не пишет Ваш собственный ошибочный Smartpointers, использует повышение (на проекте, я иду прямо сейчас, я не могу использовать повышение, и я перенес необходимость отладить мои собственные интеллектуальные указатели, я определенно не следовал бы тем же маршрутом снова, но с другой стороны прямо сейчас я не могу добавить повышение наших зависимостей)

-Tip#5, Если некоторые случайные / очень важное невыполнение (как в играх с тысячами объектов) взгляд работы на контейнер указателя повышения Thorsten Ottosen

-Tip#6 Находят заголовок обнаружения утечки для Вашей предпочтительной платформы, такой как "vld" заголовок Обнаружения Визуальной Утечки

2
ответ дан 24 November 2019 в 00:40
поделиться

Кроме того, не используйте вручную выделенную память, если существует класс библиотеки станд. (например, вектор). Удостоверьтесь, нарушаете ли Вы то правило, что у Вас есть виртуальный деструктор.

3
ответ дан 24 November 2019 в 00:40
поделиться

Я полностью подтверждаю весь совет о RAII и интеллектуальных указателях, но я также хотел бы добавить немного высокоуровневую подсказку: самая легкая память для управления является памятью, которую Вы никогда не выделяли. В отличие от языков как C# и Java, где в значительной степени все - ссылка в C++, необходимо поместить объекты на стек каждый раз, когда Вы можете. Поскольку я имею, видят, что несколько человек (включая доктора Stroustrup) указывают, главная причина, почему сборка "мусора" никогда не была популярна в C++, состоит в том, что правильно написанный C++ не производит большого количества мусора во-первых.

не пишут

Object* x = new Object;

или даже

shared_ptr<Object> x(new Object);

, когда можно просто записать

Object x;
197
ответ дан 24 November 2019 в 00:40
поделиться

Читайте на RAII и удостоверьтесь, что Вы понимаете это.

12
ответ дан 24 November 2019 в 00:40
поделиться

Вы захотите посмотреть на интеллектуальные указатели, такой как интеллектуальные указатели повышения .

Вместо

int main()
{ 
    Object* obj = new Object();
    //...
    delete obj;
}

повышение:: shared_ptr автоматически удалит, после того как подсчет ссылок является нулем:

int main()
{
    boost::shared_ptr<Object> obj(new Object());
    //...
    // destructor destroys when reference count is zero
}

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

Это не панацея, как бы то ни было. Хотя можно получить доступ к указателю базы, Вы не хотели бы передавать его третьей стороне API, если Вы не были уверены относительно того, что он делал. Много времен, Вашего материала "регистрации" к некоторому другому потоку для работы, которая будет сделана ПОСЛЕ ТОГО, КАК объем создания закончен. Это распространено с PostThreadMessage в Win32:

void foo()
{
   boost::shared_ptr<Object> obj(new Object()); 

   // Simplified here
   PostThreadMessage(...., (LPARAM)ob.get());
   // Destructor destroys! pointer sent to PostThreadMessage is invalid! Zohnoes!
}

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

25
ответ дан 24 November 2019 в 00:40
поделиться

Используйте , RAII

  • Забывает Сборку "мусора" (Используйте RAII вместо этого). Обратите внимание, что даже Сборщик "мусора" может просочиться, также (если Вы забудете "аннулировать" некоторые ссылки в Java/C#), и что Сборщик "мусора" не поможет Вам избавиться от ресурсов (если у Вас будет объект, который получил дескриптор в файл, то файл не будет освобожден автоматически, когда объект выйдет из объема, если Вы не будете делать этого вручную в Java или использовать "расположить" шаблон в C#).
  • Забывают "один возврат на функциональное" правило . Это - хороший совет C избежать утечек, но это устарело в C++ из-за его использования исключений (используйте RAII вместо этого).
  • И в то время как "Тестовый Шаблон" является хорошим советом C, это устарело в C++ из-за его использования исключений (используйте RAII вместо этого).

Это сообщение, кажется, является повторяющимся, но в C++, самый основной шаблон для знания RAII.

Учатся использовать интеллектуальные указатели, обоих от повышения, TR1 или даже непритязательного (но часто достаточно эффективный) auto_ptr (но необходимо знать его ограничения).

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

Посмотрите ниже сравнения RAII и не кода RAII:

void doSandwich()
{
   T * p = new T() ;
   // do something with p
   delete p ; // leak if the p processing throws or return
}

void doRAIIDynamic()
{
   std::auto_ptr<T> p(new T()) ; // you can use other smart pointers, too
   // do something with p
   // WON'T EVER LEAK, even in case of exceptions, returns, breaks, etc.
}

void doRAIIStatic()
{
   T p ;
   // do something with p
   // WON'T EVER LEAK, even in case of exceptions, returns, breaks, etc.
}

приблизительно RAII

Для суммирования (после комментария от [1 124] людоед Psalm33 ) RAII полагается на три понятия:

  • , После того как объект создается, он просто работает! Действительно получают ресурсы в конструкторе.
  • Объектное разрушение достаточно! Делают бесплатные ресурсы в деструкторе.
  • Это - все об объемах! Ограниченные по объему объекты (см. doRAIIStatic пример выше) будут созданы в их объявлении и будут уничтожены момент, выполнение выходит из объема, неважно, как выход (возврат, повреждение, исключение, и т.д.).

Это означает, что в корректном коде C++, большинство объектов не будет создано с new и будет объявлено на стеке вместо этого. И для те созданное использование new, все будут так или иначе , определил объем (например, присоединил к интеллектуальному указателю).

Как разработчик, это очень мощно действительно, поскольку Вы не должны будете заботиться о ручной обработке ресурса (как сделано в C, или для некоторых объектов в Java, который делает интенсивное использование try / finally для того случая)...

Редактирование (2012-02-12)

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

†“ wilhelmtell

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

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

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

(кто звонит terminate или exit в случайном коде C++?... Я не забываю иметь необходимость иметь дело с той проблемой при проигрывании с [1 113] ИЗБЫТОК : Этой библиотекой является очень C-oriented, идя до активной разработки его для создания вещей трудными для разработчиков C++ как не забота о приблизительно [1 114], стек выделил данные , или наличие "интересных" решений приблизительно [1 115] никогда возврат из их основного цикла ... Я не прокомментирую об этом) .

102
ответ дан 24 November 2019 в 00:40
поделиться

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

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

при необходимости к 'новому' в объекте затем большую часть времени, он будет иметь единственного очевидного владельца для остальной части его времени жизни. Для этой ситуации я склонен использовать набор шаблонов наборов, которые разработаны для 'владения' объектами, хранившими в них указателем. Они реализованы с вектором STL и контейнерами карты, но имеют некоторые различия:

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

Мой beaf с STL - то, что он так фокусируется на объектах Значения, в то время как в большинстве объектов приложения уникальные объекты, которым не потребовали значимой семантики копии для использования в тех контейнерах.

11
ответ дан 24 November 2019 в 00:40
поделиться

Если Вы можете/, используют интеллектуальный указатель для чего-то (хотя это должно быть огромным красным флагом), введите в своем коде с:

allocate
if allocation succeeded:
{ //scope)
     deallocate()
}

Это очевидно, но удостоверьтесь, что Вы вводите его прежде , Вы вводите любой код в объеме

2
ответ дан 24 November 2019 в 00:40
поделиться

Существует уже много о том, как не протечь, но если Вам нужен инструмент для помощи, Вы отследить утечки смотрите на:

5
ответ дан 24 November 2019 в 00:40
поделиться

Одна техника, которая стала популярной у управления памятью в C++, RAII. В основном Вы используете конструкторов/деструкторы для обработки распределения ресурсов. Конечно, существуют некоторые другие неприятные детали в C++ из-за безопасности исключения, но основная идея довольно проста.

проблема обычно сводится к одному из владения. Я настоятельно рекомендую чтение Эффективного ряда C++ Scott Meyers и современного Дизайна C++ Andrei Alexandrescu.

5
ответ дан 24 November 2019 в 00:40
поделиться

Пользовательские интеллектуальные указатели везде Вы можете! Просто уходят целые классы утечек памяти.

4
ответ дан 24 November 2019 в 00:40
поделиться

Вот еще, Вы маленькие дети и Ваши новомодные сборщики "мусора"...

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

create a thing
use that thing
destroy that thing

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

В любой программе, требующей сложных структур данных, я создаю строгое ясное дерево объектов, содержащих другие объекты - использование указателей "владельца". Этот древовидные модели основная иерархия понятий домена приложения. Пример 3D сцена владеет объектами, световыми сигналами, структурами. В конце рендеринга, когда программа выходит, существует ясный способ уничтожить все.

Много других указателей определяются по мере необходимости каждый раз, когда один объект нуждается в доступе другой, для сканирования по массивам или безотносительно; это "просто взгляд". Для 3D примера сцены - объект использует структуру, но не владеет; другие объекты могут использовать ту же самую структуру. Разрушение объекта делает не , вызывают разрушение любых структур.

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

10
ответ дан 24 November 2019 в 00:40
поделиться

Совместно используйте и знайте правила владения памяти через свой проект. Используя COM правила делают для лучшей непротиворечивости ([в] параметрах, принадлежат вызывающей стороне, вызываемый должен скопировать; параметрические усилители принадлежат вызывающей стороне, вызываемый должен сделать копию при хранении ссылки; и т.д.)

4
ответ дан 24 November 2019 в 00:40
поделиться

valgrind является хорошим инструментом для проверки утечек памяти программ во времени выполнения, также.

Это доступно на большинстве разновидностей Linux (включая Android) и на Darwin.

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

, Конечно, этот совет остаются допустимыми для любого другого инструмента проверки памяти.

4
ответ дан 24 November 2019 в 00:40
поделиться

Большой вопрос!

при использовании C++ и Вы разрабатываете ЦП-и-память в реальном времени boud приложение (как игры), необходимо записать собственного Диспетчера памяти.

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

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

  • , Маленькое Объектное Выделение было представлено Alexandrescu в 2001 в его идеальной книге "современный дизайн C++"

  • А, большое продвижение (с распределенным исходным кодом) может быть найдено в удивительной статье в Игровом Драгоценном камне Программирования 7 (2008) названным "Высокопроизводительное средство выделения "кучи"", записанное Dimitar Lazarov

  • А, которым большой список ресурсов может быть найден в этот , статья

не начинает писать новичку неполезное средство выделения собой... ДОКУМЕНТ САМОСТОЯТЕЛЬНО сначала.

8
ответ дан 24 November 2019 в 00:40
поделиться
Другие вопросы по тегам:

Похожие вопросы: