Общие рекомендации по предотвращению утечек памяти в C ++

Какие общие советы помогут избежать утечки памяти в программах на C ++? Как мне выяснить, кто должен освобождать память, которая была выделена динамически?


person Community    schedule 16.09.2008    source источник
comment
Мне это кажется довольно конструктивным.   -  person Shoerob    schedule 07.06.2015
comment
Это конструктивно. И ответы подтверждаются фактами, экспертизой, ссылками и т. Д. И посмотрите количество голосов / ответов .. !!   -  person Samitha Chathuranga    schedule 26.11.2015


Ответы (28)


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

person Community    schedule 16.09.2008
comment
Для компиляции с использованием g ++ необходимо добавить параметр: -std = c ++ 0x - person Paweł Szczur; 28.11.2012
comment
или вы можете скомпилировать с g ++, используя значение флага -std = c ++ 11 - person Prabhash Rathore; 25.03.2016

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

Не пиши

Object* x = new Object;

или даже

shared_ptr<Object> x(new Object);

когда ты можешь просто написать

Object x;
person Community    schedule 16.09.2008
comment
Хотел бы я поставить этому +10. Это самая большая проблема, с которой я сталкиваюсь сегодня у большинства программистов на C ++, и я предполагаю, что это потому, что они изучали Java раньше, чем C ++. - person Kristopher Johnson; 28.06.2009
comment
Очень интересный момент - я задавался вопросом, почему у меня проблемы с управлением памятью в C ++ возникают гораздо реже, чем на других языках, но теперь я понимаю, почему: это фактически позволяет материалам помещаться в стек, как в vanilla C. - person ArtOfWarfare; 21.01.2013
comment
Итак, что вы будете делать, если напишете Object x; а потом хочешь выбросить х? скажем, x был создан в основном методе. - person Yamcha; 11.02.2013
comment
@ user1316459 C ++ также позволяет создавать области на лету. Все, что вам нужно сделать, это заключить время жизни x в фигурные скобки, например: {Object x; x.DoSomething; }. После последнего '}' деструктор x будет вызываться, освобождая все ресурсы, которые он содержит. Если x сам по себе является памятью, которая должна быть выделена в куче, я предлагаю обернуть ее в unique_ptr, чтобы ее можно было легко и надлежащим образом очистить. - person David Peterson; 16.07.2013
comment
Итак, x выделяется в стеке, означает ли это, что если вы передадите объект x в качестве параметра, то весь объект будет скопирован? - person Robert; 12.10.2014
comment
Желаю, чтобы все данные были в стеке. А как насчет обмена данными между потоками в многопоточной программе? - person Zach; 16.04.2015
comment
Роберт: да. Росс не сказал Никогда не писать [код, содержащий новый], он сказал Не пишите [это] когда вы можете просто [положите его в стек]. Большие объекты в куче по-прежнему будут правильным вызовом в большинстве ситуаций, особенно для кода, требующего высокой производительности. - person codetaku; 26.05.2015
comment
Вам просто нужно быть осторожным с размером стопки. Контейнеры STL размещаются в куче, но этот факт скрывается реализацией. Но если вы собираетесь выделить в стеке огромный std::array, вам будет не по себе. Но ладно, это крайние случаи - person Jean-Bernard Jansen; 03.12.2015
comment
@ Роберт: Нет, это не значит. Даже если вы выделяете x в стеке, вы можете (и часто должны) писать функции, которые принимают его по ссылке. (Конечно, вы не можете вернуть его по ссылке; но оптимизация возвращаемого значения означает, что возврат по значению также не обязательно предполагает получение копии.) - person ruakh; 12.06.2016

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

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

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

Научитесь использовать интеллектуальные указатели, как из boost, 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

Подводя итог (после комментария из Ogre Psalm33), RAII опирается на три концепции:

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

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

Как разработчик, это действительно очень эффективно, поскольку вам не нужно заботиться о ручной обработке ресурсов (как это сделано в C или для некоторых объектов в Java, которые интенсивно используют _4 _ / _ 5_ для этого случая) ...

Изменить (2012-02-12)

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

- Wilhelmtell

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

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

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

(кто вызывает terminate или exit в обычном коде C ++? ... Я помню, как мне приходилось сталкиваться с этой проблемой, играя с GLUT: эта библиотека очень ориентирована на C, вплоть до того, что активно разрабатывала ее, чтобы усложнить задачу разработчикам C ++, например, не заботиться о выделенные в стеке данные или наличие" интересных "решений по никогда не возвращаются из основного цикла ... Я не буду это комментировать).

person Community    schedule 16.09.2008
comment
Разве класс T не должен использовать RAII, чтобы быть уверенным, что doRAIIStatic () не утекает памяти? Например, T p (); p.doSandwich (); Хотя я мало что знаю об этом. - person Daniel O; 25.03.2009
comment
@Ogre Psalm33: Спасибо за комментарий. Конечно, ты прав. Я добавил обе ссылки на страницу RAII Wikipedia и небольшое резюме того, что такое RAII. - person paercebal; 29.07.2010
comment
Потрясающе ... на высшем уровне! :-) - person Ogre Psalm33; 29.07.2010
comment
Как бы вы реализовали RAII для контейнера / коллекции, например списка или вектора? - person ; 26.10.2010
comment
@Shiftbit: три способа в порядке предпочтения: _ _ _ 1. Поместите реальный объект в контейнер STL. _ _ _ 2. Поместите интеллектуальные указатели (shared_ptr) на объекты в контейнер STL. _ _ _ 3. Поместите необработанные указатели в контейнер STL, но оберните контейнер, чтобы контролировать любой доступ к данным. Оболочка гарантирует, что деструктор освободит выделенные объекты, а средства доступа к оболочке будут следить за тем, чтобы ничего не сломалось при доступе / изменении контейнера. - person paercebal; 26.10.2010
comment
объекты с заданной областью ... будут разрушены ... независимо от того, какой выход не совсем верен. есть способы обмануть RAII. любой вариант terminate() будет обходить очистку. exit(EXIT_SUCCESS) - оксюморон в этом отношении. - person wilhelmtell; 14.02.2012
comment
@wilhelmtell: Спасибо за комментарий. Я отредактировал свой ответ с учетом вашего комментария. - person paercebal; 15.02.2012
comment
@paercebal ____4. Используйте контейнеры указателя ускорения - person nishantjr; 18.01.2013
comment
Хотя я полагаю, это то же самое, что и 3 - person nishantjr; 18.01.2013
comment
Когда бы вы doRAIIDynamic vrs doRAIIStatic? Правильно ли я думаю, что статический регистр размещается в куче, поэтому, если объект T большой, вы захотите использовать динамическую версию? - person Robert; 12.10.2014
comment
@Robert: В C ++ 03 вы должны использовать doRAIIDynamic в функции, которая должна передавать права собственности либо дочерней, либо родительской функции (или глобальной области видимости). Или когда вы получаете интерфейс к объекту полиморфа через фабрику (возвращая умный указатель, если он правильно написан). В C ++ 11 это не так, потому что вы можете сделать свой объект перемещаемым, поэтому проще передать право собственности на объект, объявленный в стеке ... - person paercebal; 13.10.2014
comment
@Robert: ... Обратите внимание, что объявление объекта в стеке не означает, что объект не использует кучу внутренне (обратите внимание на двойное отрицание ... :-) ...). Например, std :: string, реализованный с помощью оптимизации малых строк, будет иметь буфер в стеке класса для небольших строк (~ 15 символов) и будет использовать указатель на память в куче для строк большего размера ... Но из снаружи std :: string по-прежнему является типом значения, который вы объявляете (обычно) в стеке и используете, как если бы вы использовали целое число (в отличие от: как вы использовали бы интерфейс для класса полиморфа). - person paercebal; 13.10.2014

Вы захотите взглянуть на интеллектуальные указатели, такие как интеллектуальные указатели boost.

Вместо

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

boost :: 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!
}

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

person Community    schedule 16.09.2008

Прочтите RAII и убедитесь, что вы его понимаете.

person Community    schedule 16.09.2008

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

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

create a thing
use that thing
destroy that thing

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

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

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

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

person Community    schedule 16.09.2008
comment
Я полностью согласен. Работая во встроенной среде, вы также можете не иметь возможности пользоваться сторонними библиотеками. - person simon; 17.09.2008
comment
Я не согласен. в части использования этой вещи, если выдается возврат или исключение, вы пропустите освобождение. Что касается производительности, std :: auto_ptr вам ничего не будет стоить. Не то чтобы я никогда не кодировал так, как вы. Просто есть разница между 100% и 99% безопасным кодом. :-) - person paercebal; 17.09.2008

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

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

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

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

Мне нравится STL, поскольку он так сфокусирован на объектах Value, в то время как в большинстве приложений объекты являются уникальными сущностями, не имеющими смысловой семантики копирования, необходимой для использования в этих контейнерах.

person Community    schedule 19.09.2008

Отличный вопрос!

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

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

  • Распределитель фиксированного размера широко обсуждается повсюду в сети.

  • Размещение малых объектов было введено Александреску в 2001 году в его прекрасной книге «Современный дизайн на C ++».

  • Большой прогресс (с распределенным исходным кодом) можно найти в замечательной статье в Game Programming Gem 7 (2008) под названием «High Performance Heap allocator», написанной Димитаром Лазаровым.

  • Большой список ресурсов можно найти в этом статья

Не начинайте самостоятельно писать бесполезный распределитель памяти ... сначала ДОКУМЕНТУЙТЕ СЕБЯ.

person Community    schedule 17.09.2008

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

Проблема обычно сводится к одному из владельцев. Я настоятельно рекомендую прочитать серию «Эффективный C ++» Скотта Мейерса и «Современный дизайн C ++» Андрея Александреску.

person Community    schedule 16.09.2008

Уже есть много о том, как избежать утечек, но если вам нужен инструмент, который поможет вам отслеживать утечки, обратите внимание на:

person Community    schedule 16.09.2008
comment
BoundsChecker работает с ошибкой 404ing. - person TankorSmash; 09.02.2014

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

person Community    schedule 16.09.2008

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

person Community    schedule 16.09.2008

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

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

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

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

person Community    schedule 16.09.2008

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

person Community    schedule 16.09.2008

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

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

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

person Community    schedule 16.09.2008

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

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

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

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

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

person Community    schedule 17.09.2008

Советы в порядке важности:

-Совет №1 Всегда не забывайте объявлять деструкторы виртуальными.

-Совет № 2 Используйте RAII

-Совет № 3 Используйте смарт-указатели Boost

-Совет № 4 Не пишите свои собственные смарт-указатели с ошибками, используйте ускорение (в проекте, над которым я сейчас работаю, я не могу использовать ускорение, и мне пришлось отлаживать свои собственные интеллектуальные указатели, я бы определенно не стал тот же маршрут снова, но опять же, прямо сейчас я не могу добавить усиление нашим зависимостям)

-Совет №5. Если это некоторая случайная / не критичная для производительности (как в играх с тысячами объектов) работа, посмотрите на контейнер указателя ускорения Торстена Оттосена.

-Совет № 6 Найдите заголовок обнаружения утечек для выбранной платформы, например заголовок «vld» Visual Leak Detection.

person Community    schedule 17.09.2008
comment
Возможно, я упустил один трюк, но как могут быть слова «игра» и «не критичный к производительности» в одном предложении? - person Adam Naylor; 28.01.2009
comment
Конечно, игры - это пример критического сценария. Возможно, там не было ясно - person Robert Gould; 28.01.2009
comment
Совет №1 следует применять только в том случае, если в классе есть хотя бы один виртуальный метод. Я бы никогда не навязывал бесполезный виртуальный деструктор классу, который не предназначен для использования в качестве базового класса в полиморфном дереве наследования. - person antred; 03.03.2015

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

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

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

Эта семантика также применима к параметрам. Если вызывающий передает вам auto_ptr, они передают вам право собственности.

person Community    schedule 16.09.2008

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

Valgrind memcheck - отличная бесплатная программа.

person Community    schedule 16.09.2008

Только для MSVC добавьте следующее в начало каждого файла .cpp:

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

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

person Community    schedule 17.09.2008

valgrind (только для платформ * nix) - очень хорошая программа проверки памяти

person Community    schedule 17.09.2008

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

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

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

Все дело в владении указателем.

person Community    schedule 16.09.2008

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

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

Например, посетите этот сайт [Отладка выделения памяти в C ++] Примечание. Есть еще одна хитрость с оператором удаления:

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

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

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

person Community    schedule 16.09.2008

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

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

person Community    schedule 16.09.2008

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

person Community    schedule 16.09.2008

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

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;
}

Довольно легко, не правда ли? В случае сбоя создания потока ресурс будет освобожден (удален) с помощью auto_ptr, в противном случае право собственности будет передано потоку. Что делать, если поток настолько быстр, что после создания он освобождает ресурс до того, как

param.release();

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

Эма!

person Community    schedule 17.09.2008

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

person Community    schedule 17.09.2008

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

В противном случае слишком легко ошибиться:

new a()
if (Bad()) {delete a; return;}
new b()
if (Bad()) {delete a; delete b; return;}
... // etc.
person Community    schedule 16.09.2008
comment
Ваш ответ не соответствует приведенному здесь примеру кода? Я согласен с ответом только на один возврат, но пример кода показывает, что НЕ делать. - person simon; 17.09.2008
comment
Смысл C ++ RAII в том, чтобы избегать написанного вами кода. В языке C это, вероятно, правильное решение. Но в C ++ ваш код ошибочен. Например: что, если новый b () выдает ошибку? У вас утечка. - person paercebal; 17.09.2008