Куда уходит память, используемая для строки, когда строковой переменной присваивается новое значение? - PullRequest
3 голосов
/ 28 марта 2019

Скажите, у меня есть эта простая функция

void foo(string a, string b) {

  std::string a_string = a + a; 
  // reassign a new string to a_string
  a_string = b + b + b;

  // more stuff
}

Освобождается ли память, зарезервированная для a+a, как только a_string назначается новая строка?

Новичок в C ++ управления памятью. Я все еще оборачиваюсь вокруг него.

Ответы [ 5 ]

1 голос
/ 28 марта 2019

Зависит от короткой или длинной строки:

std::string a_string = a + a;
// reassign a new string to a_string
a_string = b + b + b; 

Во-первых, a + a создается непосредственно как a_string из-за гарантированной копии elison (c ++ 17). Здесь не происходит освобождения.

Затем, если a_string достаточно короткий, он размещается в стеке без дополнительных распределений кучи. Эта оптимизация коротких строк (SSO) выполняется большинством компиляторов, но не обязана стандартом.

Если произошел SSO, это не освобождает место в a_string, а просто использует его повторно. Память уходит в никуда:

a_string = b + b + b; 

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

Ясно видеть, куда уходит память, когда проверяется объявление std::string:

template< 
   class CharT,
   class Traits = std::char_traits<CharT>,
   class Allocator = std::allocator<CharT>
> class basic_string;

Память строки без SSO выделяется и освобождается с помощью std::allocator. Этот распределитель выделяет и освобождает память с помощью операторов new и delete. Обычно они используют malloc / free за кулисами. Так работает malloc и free.

Легко узнать, насколько большой может быть строка SSO, запустив

std::cout << std::string().capacity() << '\n';

Для clang 8.0.0 на 64-битном Intel SSO - для строк длиной до 22 символов, а для gcc 8.3 - только 15 символов.

1 голос
/ 28 марта 2019

std::string - класс менеджера ресурсов. Он владеет базовым char*. Выделенная память получает delete d, как только вызывается деструктор объекта std::string, т.е. когда сам объект выходит из области видимости.

То же самое происходит, когда std::string переназначается. Старая память будет освобождена, если она будет маленькой для новой строки и потенциально будет заменена новой памятью кучи. Классы Ressource Management, такие как std::string, имеют надежную гарантию на отсутствие утечек памяти (при условии реализации, соответствующей стандартам).

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

Другие классы управления ресурсами с такой гарантией включают умные указатели (std::unique_pointer, std::shared_pointer) и коллекции (например, std::vector, std::list, std::map ...).

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

1 голос
/ 28 марта 2019

Из того, что я знаю, строка просто копируется в память, выделенную для a_string, поскольку она более эффективна, чем освобождение памяти и выделение новой.Если память, выделенная для (a + a), меньше, чем размер, выделенный для a_string, она изменяется.

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

std :: string и автоматическое изменение размера памяти

0 голосов
/ 28 марта 2019

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

Затем a+a, которого вы действительно не можете избежать.

тогда интересная часть:

(b + b) + b, которые создают 2 выделения для 2 временных.

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

0 голосов
/ 28 марта 2019

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

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