Гарантируется ли стандартом, что 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?
Я спрашиваю, потому что полагаюсь на это, чтобы избежать фрагментации кучи.
Строки повторно используют свои буферы, когда они назначаются более коротким строкам, поэтому в вашей программе есть только одно выделение для строки. К сожалению, я не могу найти удобную цитату из стандарта на мобильном телефоне
Даже если строка действительно «спонтанно возвращает память», этого недостаточно, чтобы избежать фрагментации кучи. Строка использует распределитель (по умолчанию объект типа std::allocator<char>, но его можно изменить) для выделения и освобождения памяти, а распределитель может снова использовать механизм нижнего уровня (например, варианты операторов new и delete) для фактического выделения и освободить. Если любой этих шагов решит не освобождать память нижнему уровню, это может повлиять на фрагментацию кучи.
std::stringЕсли вам нужно гарантировать такое поведение, вы всегда можете использовать свой собственный распределитель.
@AlBundy: что за контроллер памяти?
По моему собственному опыту, если мне нужно произнести фразу «Я зависим от предотвращения фрагментации кучи», это очень хорошее время, чтобы начать обдумывать определение точных требований низкого уровня и, возможно, развертывание собственных процедур распределения.





[string.cons]/36 определяет присвоение const char*std::string в терминах назначения перемещения, определение которого:
[string.cons]/32basic_string& operator=(basic_string&& str) noexcept(/*...*/)Effects: Move assigns as a sequence container, except that iterators, pointers and references may be invalidated.
Это показывает, что Комитет позволил реализации свободно выбирать между операцией по признанию недействительной и более консервативной. И чтобы было понятнее:
[basic.string]/4References, pointers, and iterators referring to the elements of a
basic_stringsequence may be invalidated by the following uses of that basic_string object:
- (4.1) as an argument to any standard library function taking a reference to non-const
basic_stringas an argument.- (4.2) Calling non-const member functions, except
operator[],at,data,front,back,begin,rbegin,end, andrend.
I ask because i'm depending on this to avoid heap fragmentation.
std::string принимает в качестве параметра-шаблона распределитель. Если вас действительно беспокоит возможная фрагментация кучи, вы можете написать свою собственную, которая с некоторой эвристикой может иметь стратегию распределения, подходящую для ваших нужд.
На практике большинство известных мне реализаций не перераспределяют память в случае вашего вопроса. Это можно проверить путем тестирования и / или проверки документа по реализации и, в конечном итоге, исходного кода.
Вы можете себе представить, насколько нежелательной была бы такая гарантия (если предположить, что она была предоставлена), если бы вы сохранили адрес Геттисберга в строке, а затем заменили бы его на «Hello World».
Ссылка CPP указывает, что присвоение указателю на символ
Replaces the contents with those of null-terminated character string pointed to by s as if by *this = basic_string(s), which involves a call to Traits::length(s).
Это «как будто» фактически сводится к присвоению rvalue, поэтому следующий сценарий вполне возможен:
cppreference обычно надежен, но приведенный в кавычках оператор подразумевает, что существует гарантированная замена буфера, то есть чушь. В остальном это хорошая концептуальная модель. Но это просто незначительно проще, чем описание стандарта, указанное в ответе YSC, что поэтому было бы предпочтительнее.
@ Cheersandhth.-Альф Правда? Он говорит «как если бы», что в Стандарте всегда означает «в отношении наблюдаемых побочных эффектов, за исключением немногих побочных эффектов построений / разрушений».
@rustyx тоже можно. Вы можете представить это как временное похищение существующего распределения с последующим его возвратом.
@rustyx Стандарт просто говорит об эффекте. Учитывая, что эффект x = "hi"; такой же, как эффект x.assign("hi");, реализация будет эффективна для обоих. libstdC++, например, имеет свой operator=(const CharT*), просто вызывающий assign напрямую.
Если ваши строки короткие (до 15 или 22 байтов, в зависимости от компилятора / std lib) и вы используете относительно недавний компилятор в режиме C++ 11 или более поздней версии, то вы, вероятно, выиграете от Оптимизация коротких строк (SSO) . В этом случае содержимое строки отдельно не выделяется в куче.
Эта ссылка также содержит много подробностей об общих реализациях и стратегиях распределения.
Однако обе строки в вашем примере слишком длинные для SSO.
Гарантировано ли это стандартом? Гарантируется ли SSO стандартом?
@pipe Нет, это не то, что Пол имел в виду в: "вы вероятно, чтобы воспользоваться оптимизацией коротких строк (SSO)".
@YSC Хорошо, но вопрос довольно конкретный, и OP уже знает, что это может произойти, так как он зависит от него, поэтому я не понимаю, на что это на самом деле отвечает.
@pipe Я согласен, что этот ответ немного неверен, но на него ответили сразу после того, как OP задал свой вопрос. В то время вопрос не был помечен как языковед, и после этого он будет отредактирован. Это ответ на часть проблемы OP (mem frag) и дает интересную информацию. Я проголосовал за то, что он стоит.
Это не должно быть исчерпывающим ответом, это просто дополнение к случаю SSO. Я думаю, что ссылка довольно интересна с точки зрения практических мер производительности std :: string.
Is it guaranteed by the standard that std::string will not give back allocated memory spontaneously if reassigned from a string of a smaller size?
Независимо от фактического ответа (т.е. «Нет, без гарантии») вы должны руководствоваться следующим принципом: Если не очевидно, что это так, не думайте, что это так.
В вашем конкретном случае - если вам нужен жесткий контроль над поведением кучи, вы можете вообще не использовать std::string (возможно, это зависит от обстоятельств). И вы можете не захотеть использовать распределитель по умолчанию (опять же, возможно); и вы можете захотеть запоминать строки; и т. д. Что вам обязательно нужно сделать, так это делать меньше предположений, измерять, если возможно, и иметь четкий дизайн, гарантирующий, что ваши потребности будут удовлетворены.
если выделение новой строки меньше предыдущего, выделение не происходит. Если для новой строки требуется больше выделения, чем для текущей, происходит перераспределение. Подобно std :: vector.