C ++ деталь динамической памяти - PullRequest
4 голосов
/ 29 июня 2009

Я программист на C и Java, поэтому распределение памяти и ООП не являются чем-то новым для меня. Но я не уверен, как именно избежать утечек памяти при реализации C ++ объектов. А именно:

string s1("0123456789");
string s2 = s1.substr(0,3);

s2 теперь имеет новый строковый объект, поэтому он должен быть освобожден с помощью:

delete &s2;

Правильно?

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

Ответы [ 8 ]

16 голосов
/ 29 июня 2009

номер

Вам нужно только освободить выделенную память (т.е. через new или memalloc).

12 голосов
/ 29 июня 2009

Нет,

И s1, и s2 будут уничтожены, когда выйдут за рамки.

s1.substr() создаст временный объект, о котором вам не нужно думать.

8 голосов
/ 29 июня 2009

В ответах есть несколько слоев.

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

  • В качестве локальных переменных (либо внутри функции, либо в качестве членов класса). Примеры: int i или int i = 42. Они имеют автоматическую продолжительность хранения (эти примеры технически сокращены для auto int i или auto int i = 42, хотя ключевое слово auto практически никогда не используется. Это означает, что эти переменные будут автоматически освобождены, когда они выйдут из области видимости. Их деструктор гарантированно будет вызван (независимо от того, как вы выходите из области, будь то из функции, возвращаемой функцией или с помощью исключения, их деструктор будет вызываться, когда они выходят из области действия, а затем используемая ими память освобождается) . Локальные переменные, объявленные так, расположены в стеке.
  • Статические переменные (с ключевым словом static, подразумевающими статическую длительность хранения, в отличие от автоматического, показанного выше. Они просто остаются на время действия программы, поэтому их не нужно освобождать
  • в куче, с динамическим распределением (new int или new int(42)). Их нужно освободить вручную, позвонив по номеру delete.

Так что на самом низком уровне вам просто нужно сохранить симметрию. Если что-то было выделено с new, освободите его с помощью delete, malloc is freed by free , and new [] by delete [] `. И переменные, которые объявлены без каких-либо из них, автоматически обрабатываются и не должны освобождаться вручную.

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

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

Это означает, что вам, по сути, никогда не придется звонить delete в коде верхнего уровня. Он практически всегда будет скрыт за деструктором объекта RAII. new вызовы также становятся редкими, но все еще используются вместе с интеллектуальными указателями (такими как: boost::shared_ptr<int>(new int(42)), который динамически выделяет целое число, а затем передает его интеллектуальному указателю, который берет на себя владение им и очищает его автоматически .

5 голосов
/ 29 июня 2009

s1 и s2 автоматически распределяются. Вы не удаляете их. Вы только delete объекты, которые вы создали, используя new.

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

3 голосов
/ 29 июня 2009

Как и все остальные сказали - здесь не нужно удалять. Если вы не видите new, вам (как правило) не нужно delete. Я хочу добавить, что s2 не имеет нового строкового объекта. s2 является строковым объектом с момента его объявления. При назначении фрагмента s1 s2 значение s2 изменяется так, что оно содержит те же символы, что и в подстроке.

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

1 голос
/ 29 июня 2009

Одной из наиболее важных концепций, которые нужно понять в современном C ++ (особенно из c-фона), является RAII . C ++ инкапсулирует ресурсы, такие как память (или мьютексы, или транзакции), внутри классов, которые «приобретают» ресурс при создании (строковый конструктор выделяет динамическую память) и «освобождают» его при уничтожении (уничтожение строкового класса освобождает его). Так как уничтожение стековых объектов является детерминированным (истекает срок действия стекового объекта, когда это происходит в закрытой области видимости), выпуск не должен быть записан, и произойдет, даже если выдается исключение.

Итак, нет. В большинстве моих кодов я никогда не записываю явное удаление (или удаление []), потому что ресурсы управляются либо строкой, конкатайном STL, либо shared_ptr или scoped_ptr.

1 голос
/ 29 июня 2009

Нет, s2 не нужно удалять вручную. Это локальная переменная в стеке, которая будет автоматически уничтожена, как только выйдет из области видимости, точно так же, как она была автоматически выделена при объявлении. Как правило, вы только delete вещи, которые вы выделили с new.

Временные объекты, возвращаемые функциями, управляются автоматически и уничтожаются к концу оператора - если они нужны дольше, они обычно копируются в локальную переменную перед уничтожением временного (например, с помощью простого присваивания, такого как Object o = f(); или как в строке с вызовом substr() в вашем примере).

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

1 голос
/ 29 июня 2009

почему вы должны освободить s2 вручную? удаление динамической памяти в s2 будет выполняться деструктором std :: string.

...