Использование для значения емкости строки

В стандартной библиотеке C++ std::string имеет общедоступную функцию-член capacity(), которая возвращает размер внутреннего выделенного хранилища, значение, большее или равное количеству символов в строке (согласно здесь). Для чего можно использовать это значение? Это как-то связано с пользовательскими распределителями?


person dreamlax    schedule 21.05.2010    source источник


Ответы (7)


Скорее всего, вы будете использовать функцию-член reserve(), которая устанавливает емкость, по крайней мере, на указанное значение.

Сама функция-член capacity() может использоваться, чтобы избежать выделения памяти. Например, вы можете перерабатывать использованные строки через пул и помещать каждую в корзину разного размера в зависимости от ее емкости. Затем клиент пула может запросить строку, которая уже имеет некоторую минимальную емкость.

person Marcelo Cantos    schedule 21.05.2010
comment
Раньше я никогда не думал о повторном использовании строк. Распространена ли такая оптимизация? Возможно, в системах с очень ограниченными ресурсами. - person dreamlax; 21.05.2010
comment
Это однозначно оптимизация. Выделение и освобождение памяти может быть дорогостоящим. Итак, в программах, где это оказалось узким местом, программисты, как известно, создают пулы памяти, а не всегда выделяют или освобождают память. Операции со строками в целом довольно дороги (особенно, когда довольно часто создаются и удаляются строки довольно быстро), они были бы главной целью. Однако это не то, что вы бы сделали, если бы профилирование не показало, что это действительно улучшит производительность. - person Jonathan M Davis; 21.05.2010
comment
Это, конечно, не является распространенным явлением, поскольку очень немногие приложения предъявляют такие высокие требования к производительности. Кроме того, речь идет скорее о том, чтобы избежать перегрузки malloc, чем об ограничениях памяти, особенно в приложениях с большим количеством параллельных вычислений, которые страдают от снижения производительности при слишком частом манипулировании кучей. - person Marcelo Cantos; 21.05.2010
comment
Обычный подход заключается не в наличии пулов строк, а в наличии пользовательского распределителя с пулами памяти, который выполняет быстрое выделение и может использоваться со всеми типами, а не только со строками. На самом деле я бы вообще не рассматривал пул строк как оптимизацию... - person David Rodríguez - dribeas; 21.05.2010
comment
Чтобы пул строк работал, строки должны находиться в куче. Большинство строк на самом деле являются членами другой структуры или находятся в стеке. Выделенный распределитель — гораздо лучшее решение, так как строковый буфер часто находится в куче (по модулю SSO). - person MSalters; 21.05.2010
comment
О, и это не сработало бы в любом случае. std::string не должен учитывать capacity при присвоении, он может перераспределяться для сжатия. - person MSalters; 21.05.2010
comment
Пользовательские распределители довольно проблематичны (в основном из-за того, что они являются частью типа строки) и непрактичны для использования во многих ситуациях. Я избегаю их, как чумы. - person Marcelo Cantos; 21.05.2010
comment
Вы бы не назначали строку обратно в пул, а только указатель или интеллектуальный указатель. - person Marcelo Cantos; 21.05.2010
comment
Вы опередили Нила Баттерворта на 2 минуты, описав использование capacity(), которое кажется правильным (т. е. пытаетесь избежать дорогостоящего перераспределения). - person dreamlax; 27.05.2010

Функция string::capacity() возвращает общее количество символов, которое может содержать std::string, прежде чем ей потребуется перераспределить память, что является довольно дорогостоящей операцией.

std::vector работает таким же образом, поэтому я предлагаю вам посмотреть std::vector здесь для подробного объяснения разницы между выделенной и инициализированной памятью.

Обновить

Хм, я вижу, что неправильно понял вопрос, на самом деле я думаю, что сам никогда не использовал capacity() ни для std::string, ни для std::vector, кажется, это редко имеет какую-либо причину, поскольку вам все равно нужно вызывать резерв.

person Viktor Sehr    schedule 21.05.2010

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

person Community    schedule 21.05.2010

Его можно использовать для некоторой настройки производительности, если вы собираетесь добавить в строку много символов. Перед началом манипуляций со строкой вы можете проверить емкость и, если она слишком мала, зарезервировать желаемую длину за один шаг (вместо того, чтобы позволить ему последовательно перераспределять большие куски памяти несколько раз, что приведет к снижению производительности).

person Péter Török    schedule 21.05.2010
comment
Ясно, но почему бы просто не вызвать резерв в первую очередь (и забыть о емкости), просто чтобы убедиться? Я имею в виду, если вы все равно собираетесь вставлять символы, имеет ли значение, знаете ли вы емкость заранее? - person dreamlax; 21.05.2010
comment
@dreamlax Ну, я считаю, что реализация reserve() может выделить больше памяти, даже если в этом нет необходимости, и хотя capacity(), скорее всего, встроена, reserve() с меньшей вероятностью, поскольку она будет больше (поэтому вызов capacity() может быть дешевле в случае, когда вам не понадобится reserve()). Однако на самом деле, скорее всего, дело в полноте. Кто-то может захотеть узнать емкость, и у него может быть действительно веская причина, по которой разработчики библиотеки не могли ее предвидеть. Хотя обычно это не нужно, иметь его не помешает, и это может быть полезно. - person Jonathan M Davis; 21.05.2010

Строки имеют емкость и размер. Емкость указывает, сколько символов может содержать строка, прежде чем ей потребуется выделить больше памяти. Размер указывает, сколько символов он в настоящее время содержит. reserve() можно использовать для установки минимальной емкости строки (будет выделена память для минимум этого количества символов, но может быть выделено и больше).

Это в первую очередь важно, если вы увеличиваете размер строки. Когда вы объединяете строку с помощью += или append(), символы из данной строки будут добавлены в конец текущей. Если увеличение строки до этого размера не превышает емкость, то она просто будет использовать имеющуюся емкость. Однако, если новый размер превысит текущую емкость, тогда строка должна будет перераспределить внутреннюю память и скопировать свои внутренние компоненты в новую память. Если вы собираетесь делать это часто, это может стать дорогостоящим (хотя это делается в амортизированное постоянное время), поэтому в таком случае вы можете использовать reserve() для предварительного выделения достаточного количества памяти, чтобы уменьшить частоту перераспределения. .

векторные функции в основном одинаковы с теми же функциями.

Лично, хотя я время от времени имел дело с capacity() и reserve() с вектором, я никогда не видел особой необходимости делать это со строкой - вероятно, потому, что я обычно не делаю достаточно конкатенаций строк в своем коде, чтобы это стоило Это. В большинстве случаев конкретная строка может получить несколько конкатенаций, но этого недостаточно, чтобы беспокоиться о ее емкости. Как правило, вы беспокоитесь о емкости, когда пытаетесь оптимизировать свой код.

person Jonathan M Davis    schedule 21.05.2010
comment
Я понимаю использование reserve, но почему важно знать текущую емкость строки? - person dreamlax; 21.05.2010
comment
Возможно, вы знаете, какая емкость вам понадобится, и хотели бы проверить, достаточно ли велика строка, которую вы собираетесь добавить. Если вы выполняете несколько конкатенаций и знаете, сколько символов это будет, было бы более эффективно сначала просто вызвать reserve() и избежать множественных перераспределений. Но вы не обязательно знали бы, нужно ли вам это, если бы вы сначала не проверили емкость. Однако в большинстве случаев вы, вероятно, будете иметь дело с только что созданной строкой и просто вызовете reserve(), не беспокоясь о capacity(). - person Jonathan M Davis; 21.05.2010

Вряд ли есть какое-либо релевантное использование. Это похоже на std::vector::capacity. Однако одним из наиболее распространенных применений строк является присваивание. При назначении std::string его .capacity может измениться. Это означает, что реализация имеет право игнорировать старую емкость и точно выделять достаточно памяти.

person MSalters    schedule 21.05.2010

Это действительно не очень полезно и, вероятно, существует только для симметрии с vector (при условии, что оба будут работать внутри одинаково).

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

Однако для string такой гарантии нет. Кажется, что емкость предназначена только для информационных целей, хотя даже это с натяжкой, поскольку в любом случае не похоже, что из нее можно извлечь какую-либо полезную информацию. (Хуже того, непрерывность строковых символов также не гарантируется, поэтому единственный способ получить строку в виде линейного буфера — это c_str(), что может привести к перераспределению.)

Предположительно, string изначально предполагалось реализовать как некий частный случай vector, но со временем они разошлись...

person Community    schedule 21.05.2010