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


130

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


26
Мне это кажется довольно конструктивным.
Shoerob

11
Это конструктивно. И ответы подтверждаются фактами, экспертизой, ссылками и т.д. И посмотрите количество голосов / ответов .. !!
Samitha Chathuranga

Ответы:


40

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


1
Для компиляции с использованием g ++ необходимо добавить параметр: -std = c ++ 0x
Paweł Szczur

или вы можете скомпилировать с g ++, используя значение флага -std = c ++ 11
Прабхаш Ратхор

200

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

Не пиши

Object* x = new Object;

или даже

shared_ptr<Object> x(new Object);

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

Object x;

34
Хотел бы я поставить этому +10. Это самая большая проблема, с которой я сталкиваюсь сегодня у большинства программистов на C ++, и я предполагаю, что это потому, что они изучали Java раньше, чем C ++.
Кристофер Джонсон

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

Итак, что вы будете делать, если напишете Object x; а потом хочешь выбросить х? скажем, x был создан в основном методе.
Yamcha

3
@ user1316459 C ++ также позволяет создавать области на лету. Все, что вам нужно сделать, это заключить время жизни x в фигурные скобки, например: {Object x; x.DoSomething; }. После последнего '}' деструктор x будет вызываться, освобождая все ресурсы, которые он содержит. Если x сам по себе является памятью, которая должна быть выделена в куче, я предлагаю обернуть ее в unique_ptr, чтобы ее можно было легко и надлежащим образом очистить.
Дэвид Петерсон

1
Роберт: да. Росс не сказал: «Никогда не пишите [код, содержащий новый]», он сказал: «Не пишите [это], если вы можете просто [положить его в стек]». Большие объекты в куче по-прежнему будут правильным вызовом в большинстве ситуаций, особенно для кода, требующего высокой производительности.
codetaku,

104

Использовать 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, которые интенсивно используют try/ finallyв этом случае) ...

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

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

- Вильгельмтель

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

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

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

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


Разве класс T не должен использовать RAII, чтобы быть уверенным, что doRAIIStatic () не утечка памяти? Например, T p (); p.doSandwich (); Хотя я мало что знаю об этом.
Daniel O

@Ogre Psalm33: Спасибо за комментарий. Конечно, ты прав. Я добавил обе ссылки на страницу RAII Wikipedia и небольшое резюме того, что такое RAII.
paercebal

1
@Shiftbit: три способа в порядке предпочтения: _ _ _ 1. Поместите реальный объект в контейнер STL. _ _ _ 2. Поместите интеллектуальные указатели (shared_ptr) на объекты в контейнер STL. _ _ _ 3. Поместите необработанные указатели в контейнер STL, но оберните контейнер, чтобы контролировать любой доступ к данным. Оболочка гарантирует, что деструктор освободит выделенные объекты, а средства доступа к оболочке будут следить за тем, чтобы при доступе / изменении контейнера ничего не было нарушено.
paercebal

1
@Robert: В C ++ 03 вы должны использовать doRAIIDynamic в функции, которая должна передавать права собственности дочерней или родительской функции (или глобальной области видимости). Или когда вы получаете интерфейс к объекту полиморфа через фабрику (возвращая умный указатель, если он правильно написан). В C ++ 11 это не так, потому что вы можете сделать свой объект подвижным, поэтому проще
передать

2
@Robert: ... Обратите внимание, что объявление объекта в стеке не означает, что объект не использует кучу внутренне (обратите внимание на двойное отрицание ... :-) ...). Например, std :: string, реализованный с помощью оптимизации малых строк, будет иметь буфер «в стеке класса» для небольших строк (~ 15 символов) и будет использовать указатель на память в куче для более крупных строк ... Но снаружи std :: string по-прежнему является типом значения, который вы объявляете (обычно) в стеке и используете, как если бы вы использовали целое число (в отличие от: как вы использовали бы интерфейс для класса полиморфа).
paercebal

25

Вы захотите взглянуть на интеллектуальные указатели, такие как интеллектуальные указатели 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!
}

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



11

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

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

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

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

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


10

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

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

create a thing
use that thing
destroy that thing

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

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

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

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


Я полностью согласен. Работая во встроенной среде, вы также можете не иметь возможности пользоваться сторонними библиотеками.
Саймон

6
Я не согласен. в части «использовать эту вещь», если выдается возврат или исключение, вы пропустите освобождение. Что касается производительности, std :: auto_ptr вам ничего не стоит. Не то чтобы я никогда не кодировал так, как вы. Просто есть разница между 100% и 99% безопасным кодом. :-)
paercebal

8

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

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

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

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

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

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

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

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


5

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

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


5

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


BoundsChecker работает с ошибкой 404ing.
ТанкорСмаш

4

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


4

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


4

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

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

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

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


3

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


2

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

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

Это очевидно, но не забудьте ввести его, прежде чем вводить какой-либо код в области видимости.


2

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

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

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

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

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


2

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

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

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

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

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

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

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


Возможно, я упустил один трюк, но как могут быть слова «игра» и «не критичный к производительности» в одном предложении?
Адам Нейлор

Конечно, игры - это пример критического сценария. Возможно, там не удалось прояснить ситуацию
Роберт Гулд

Совет №1 следует применять только в том случае, если в классе есть хотя бы один виртуальный метод. Я бы никогда не навязывал бесполезный виртуальный деструктор классу, который не предназначен для использования в качестве базового класса в полиморфном дереве наследования.
antred 03

1

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

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

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

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


1

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

Valgrind memcheck - отличный бесплатный инструмент .


1

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

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

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



1

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

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

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

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


1
  • Старайтесь избегать динамического размещения объектов. Пока классы имеют подходящие конструкторы и деструкторы, используйте переменную типа класса, а не указатель на него, и вы избегаете динамического выделения и освобождения, потому что компилятор сделает это за вас.
    Фактически, это также механизм, используемый «умными указателями» и называемый RAII некоторыми другими авторами ;-).
  • Когда вы передаете объекты другим функциям, предпочитайте ссылочные параметры указателям. Это позволяет избежать некоторых возможных ошибок.
  • По возможности объявляйте параметры const, особенно указатели на объекты. Таким образом, объекты не могут быть освобождены «случайно» (кроме случаев, когда вы отбрасываете константу ;-))).
  • Сведите к минимуму количество мест в программе, в которых вы выполняете выделение и освобождение памяти. E. g. если вы выделяете или освобождаете один и тот же тип несколько раз, напишите для него функцию (или фабричный метод ;-)).
    Таким образом, вы можете легко создавать отладочные данные (адреса которых выделяются и освобождаются, ...), если это необходимо.
  • Используйте фабричную функцию для выделения объектов нескольких связанных классов из одной функции.
  • Если ваши классы имеют общий базовый класс с виртуальным деструктором, вы можете освободить их все, используя одну и ту же функцию (или статический метод).
  • Проверьте свою программу с помощью таких инструментов, как purify (к сожалению, много $ / € / ...).

0

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

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

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

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

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

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


0

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

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


0

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


0

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

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 ++, не так ли? Ура,

Эма!


0

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


-3

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

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

new a()
if (Bad()) {delete a; return;}
new b()
if (Bad()) {delete a; delete b; return;}
... // etc.

Ваш ответ не соответствует приведенному здесь примеру кода? Я согласен с ответом «только один возврат», но пример кода показывает, чего НЕ делать.
Саймон

1
Смысл C ++ RAII в том, чтобы избегать написанного вами кода. В C это, наверное, правильное решение. Но в C ++ ваш код ошибочен. Например: что, если новый b () выдает ошибку? У вас утечка.
paercebal
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.