Является ли std :: string гарантией не возвращать память спонтанно?

Гарантируется ли стандартом, что std::string не будет возвращать выделенную память спонтанно, если переназначить из строки меньшего размера?

Другими словами:

std::string str = "Some quite long string, which needs a lot of memory";
str = "";
str = "A new quite long but smaller string"; // Guaranteed to not result in a heap allocation?

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

Ответы

Ответ 1

Никакой гарантии вообще.

[string.cons]/36 определяет назначение const char* в std::string в [string.cons]/36, определение которой:

[string.cons]/32

basic_string& operator=(basic_string&& str)  noexcept(/*...*/)

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

Это свидетельствует о том, что Комитет позволил осуществлению выбора свободно выбирать между недействительной операцией и более консервативной. И сделать еще более ясными:

[basic.string]/4

Ссылки, указатели и итераторы, относящиеся к элементам последовательности basic_string, могут быть аннулированы следующими видами использования этого объекта basic_string:

  • (4.1) в качестве аргумента любой стандартной библиотечной функции, ссылающейся на не-const basic_string в качестве аргумента.
  • (4.2) Вызов неконстантных функций-членов, кроме operator[], at, data, front, back, begin, rbegin, end и rend.

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

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

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

Ответ 2

Ссылка CPP указывает, что назначение указателю на символ

Заменяет содержимое символами строки с нулевым символом, на которые указывает s, как будто: * this = basic_string (s), который включает вызов Traits :: length (s).

Это "как будто" на самом деле сводится к присвоению rvalue, поэтому следующий сценарий вполне возможен:

  1. Создается свежая временная строка.
  2. Эта строка перехватывает содержимое, как при присвоении значению rvalue.

Ответ 3

Гарантируется ли стандартом, что std :: string не будет возвращать выделенную память спонтанно, если переназначить из строки меньшего размера?

Независимо от фактического ответа (который есть "Нет, нет гарантии"), вы должны использовать следующий принцип: если не очевидно, что это должно быть так, то не предполагайте, что это так.

В вашем конкретном случае - если вы хотите жестко контролировать поведение кучи, вы даже не можете вообще использовать std::string (возможно, это зависит). И вы, возможно, не захотите использовать распределитель по умолчанию (опять же, возможно); и вы можете захотеть запоминать строки; и т.д. То, что вы должны абсолютно делать, - это сделать меньше допущений, по возможности мерить и иметь явный дизайн для обеспечения ваших потребностей.

Ответ 4

Если ваши строки короткие (до 15 или 22 байта, в зависимости от компилятора /std lib), и вы используете относительно недавний компилятор в режиме С++ 11 или более поздней версии, тогда вы, вероятно, выиграете от оптимизации коротких строк ( SSO). В этом случае содержимое строки не выделяется отдельно в куче.

Эта ссылка также содержит подробную информацию об общих реализациях и стратегиях распределения.

Тем не менее, обе строки в вашем примере слишком длинны для SSO.