Запуск C ++ проекта. Стоит ли беспокоиться об освобождении динамически выделенной памяти? - PullRequest
4 голосов
/ 27 апреля 2010

Я довольно опытный в C, и освобождение памяти в C является обязательным.

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

Где мне прочитать об этом? Является ли это ценной заменой правильной функциональности delete C ++? Как это работает?

EDIT

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

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

Другие говорят, что если я использую new , я также должен использовать delete , как C.

Так какой метод считается более стандартным и чаще используется?

Ответы [ 14 ]

18 голосов
/ 27 апреля 2010

Где мне прочитать об этом?

Херб Саттер Исключительный C ++ и Скотт Мейерс Более эффективный C ++ - обе превосходные книги, которые подробно освещают эту тему.

Существует также много дискуссий в Интернете (Google или StackOverflow ищет «RAII» или «умный указатель», несомненно, даст много хороших результатов).

Является ли это ценной заменой для правильной delete функциональности C ++?

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

9 голосов
/ 27 апреля 2010

Что я имел в виду в своем комментарии (извините за краткость - мне пришлось бежать в магазины), так это то, что вы должны использовать:

std::string s = "foobar";

вместо:

std::string * s = new std::string( "foobar" );
...
delete s;

и

vector <Person> p;
p.push_back( Person( "fred" ) );

вместо:

vector <Person *> p;
p.push_back( new Person( "fred" ) );

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

5 голосов
/ 27 апреля 2010

Если вы выделяете динамическую память (с новой), вам нужно освободить ее (с помощью delete), точно так же, как использовать malloc / free в C. Сила C ++ в том, что она дает вам много способов НЕ вызывать новую, в в этом случае вам не нужно звонить удалить.

4 голосов
/ 27 апреля 2010

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

MyClass *y = new MyClass;
doSomething(y);
delete y;

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

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

Обратите внимание, что auto_ptr не является "общим" указателем. Это объект, который берет на себя ответственность за динамически размещенный объект и отдает это право собственности при назначении и копировании. Он не считает ссылки на память. Это делает его непригодным для использования в стандартных контейнерах, и многие в целом предпочитают shared_ptr Boost вместо auto_ptr, предоставляемого стандартом.

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

Проблема в том, что auto_ptr не вполне соответствует требованиям вида Вы можете положить в контейнеры, потому что копии auto_ptrs не являются эквивалент. Во-первых, есть ничего, что говорит вектор не может просто принять решение и сделать "дополнительный" внутренняя копия какого-то объекта это содержит. Для другого, когда вы звоните универсальные функции, которые будут копировать элементы, как sort () делает, функции должны быть в состоянии предположить, что копии будут эквивалент. По крайней мере, один популярный сорт внутренне берет копию "опоры" элемент, и если вы попытаетесь сделать это работать на auto_ptrs будет весело копия объекта pivot auto_ptr (тем самым принимая на себя ответственность и положить это во временном auto_ptr на сторона), сделайте остальную часть своей работы на последовательность (включая дальнейшее копии теперь не владеющего auto_ptr это было выбрано в качестве основного значения), и когда сортировка закончена вокруг оси уничтожен и у вас возникла проблема: при хотя бы один auto_ptr в последовательности (тот, который был опорным значением) нет дольше владеет указателем, который он когда-то держал, и на самом деле указатель он имел уже был удален!

Взято из: Эффективно используя auto_ptr

2 голосов
/ 27 апреля 2010

Это отличный вопрос, и фактически несколько в одном:

Нужно ли беспокоиться об управлении памятью?

Да! В C ++ нет сборки мусора. Каждый раз, когда вы выделяете что-то с помощью new, вам нужно либо вызвать delete в своем собственном коде, либо передать эту ответственность чему-то вроде умного указателя .

Когда я должен использовать динамическое выделение памяти?

Причины, по которым вы хотите использовать динамическое выделение памяти (выделение с помощью new). Вот некоторые из них:

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

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

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

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

Например, boost :: scoped_ptr освободит для вас память, когда она выйдет за пределы

{
   scoped_ptr<MyClass> myVar( new MyClass() );

   // do Something with myVar

} // myVar goes out of scope and calls delete on its MyClass

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

Умные указатели бывают разных форм, включая:

Если бы вы могли использовать умные указатели Boost, я бы. Они качаются!

2 голосов
/ 27 апреля 2010

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

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

2 голосов
/ 27 апреля 2010
2 голосов
/ 27 апреля 2010

Ну, конечно, вам нужно удалить. Я бы перефразировал это как «какие библиотеки я могу использовать, чтобы автоматизировать удаление выделенной памяти?». Я бы порекомендовал вам начать с чтения страницы Boost Smart указатели .

1 голос
/ 27 апреля 2010

Прежде всего, прежде чем приступить к использованию auto_ptr и написанию собственных классов RAII, научитесь пользоваться стандартной библиотекой шаблонов. Он предоставляет много общих контейнерных классов, которые автоматически выделяют их внутреннюю память, когда вы создаете их экземпляры, и освобождаете ее, когда они выходят из области видимости - такие как векторы, списки, карты и т. Когда вы используете STL, использование оператора new и delete (или malloc и free) редко требуется.

1 голос
/ 27 апреля 2010

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

Тем не менее, вы можете использовать умные указатели, чтобы облегчить проблему необходимости вручную освобождать память через delete - например, см. Умные Ponters (повышение) .

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