Функции std:: strings capacity(), reserve() & resize()
Я хотел использовать std::string просто для создания динамического буфера и для повторения его с помощью индекса. Является ли resize() единственной функцией для фактического выделения буфера?
Я попробовал reserve(), но когда я пытаюсь получить доступ к строке через индекс, он утверждает. Кроме того, когда размер строки по умолчанию составляет 15 байт (в моем случае), но если я все еще не могу получить к нему доступ как my_string[1]
.
Итак, емкость строки не является фактическим буфером? Также reserve() также не выделяет фактический буфер?
string my_string;
// I want my string to have 20 bytes long buffer
my_string.reserve( 20 );
int i = 0;
for ( parsing_something_else_loop )
{
char ch = <business_logic>;
// store the character in
my_string[i++] = ch; // this crashes
}
Если я сделаю resize() вместо reserve(), это будет отлично. Как получается, что строка имеет емкость, но не может реально получить к ней доступ с помощью []? Разве это не значит, что размер зарезервирован(), чтобы вы могли получить к нему доступ?
Дополнительные
В ответ на ответы, я хотел бы спросить людей stl. Почему кто-то использовал reserve(), когда resize() делает то же самое, а также инициализирует строку? Должен сказать, что я не очень ценю аргумент производительности в этом случае. Все, что resize() делает дополнительным к тому, что делает backup(), состоит в том, что он просто инициализирует буфер, который, как мы знаем, всегда приятно делать в любом случае. Можем ли мы проголосовать за резерв() с острова?
Ответы
Ответ 1
Разве это не значит, что размер резервирования() будет таким, чтобы вы могли получить к нему доступ?
Нет, что точка resize()
.
reserve()
дает достаточно места, чтобы будущий вызов, который приводит к увеличению размера (например, вызов push_back()
), будет более эффективным.
Из вашего варианта использования, похоже, вы должны использовать .push_back()
.
my_string.reserve( 20 );
for ( parsing_something_else_loop )
{
char ch = <business_logic>;
my_string.push_back(ch);
}
Как получается, что строка имеет емкость, но не может получить к ней доступ с помощью []?
Вызов .reserve()
- это как взорвать горы, чтобы дать вам свободную землю. Количество свободной земли - это .capacity()
. Земля есть, но это не значит, что вы можете там жить. Вы должны построить дома, чтобы двигаться. Количество домов - это .size()
(= .length()
).
Предположим, вы строите город, но после строительства 50-го вы обнаружили, что земли недостаточно, поэтому вам нужно найти другое место, достаточно большое, чтобы соответствовать 51-му дому, а затем мигрировать там все население. Это крайне неэффективно. Если бы вы знали, что вам нужно построить 1000 домов вверх, то вы можете позвонить
my_string.reserve(1000);
чтобы получить достаточно земли, чтобы построить 1000 домов, а затем вы вызываете
my_string.push_back(ch);
чтобы построить дом с назначением ch
в это место. Емкость 1000, но размер все равно 1. Вы не можете сказать
my_string[16] = 'c';
потому что дом № 16 еще не существует. Вы можете позвонить
my_string.resize(20);
чтобы получить дома # 0 ~ # 19, построенные за один раз, поэтому
my_string[i++] = ch;
работает нормально (до тех пор, пока 0 ≤ я ≤ 19).
См. также http://en.wikipedia.org/wiki/Dynamic_array.
Для вашего дополнительного вопроса
.resize()
не может полностью заменить .reserve()
, потому что (1) вам не всегда нужно использовать все выделенные пространства и (2) построение по умолчанию + назначение копии - это двухэтапный процесс, который может занять больше (например, для больших объектов), т.е.
#include <vector>
#include <unistd.h>
struct SlowObject
{
SlowObject() { sleep(1); }
SlowObject(const SlowObject& other) { sleep(1); }
SlowObject& operator=(const SlowObject& other) { sleep(1); return *this; }
};
int main()
{
std::vector<SlowObject> my_vector;
my_vector.resize(3);
for (int i = 0; i < 3; ++ i)
my_vector[i] = SlowObject();
return 0;
}
Отработает от вас по меньшей мере 9 секунд для запуска, а
int main()
{
std::vector<SlowObject> my_vector;
my_vector.reserve(3);
for (int i = 0; i < 3; ++ i)
my_vector.push_back(SlowObject());
return 0;
}
отходит только 6 секунд.
std::string
здесь только экземпляр std::vector
.
Ответ 2
Нет - точка reserve
предназначена для предотвращения перераспределения. resize
устанавливает полезный размер, reserve
- нет - он просто устанавливает объем пространства, который зарезервирован, но еще не используется непосредственно.
Здесь один пример - мы собираемся создать случайную строку из 1000 символов:
static const int size = 1000;
std::string x;
x.reserve(size);
for (int i=0; i<size; i++)
x.push_back((char)rand());
reserve
- это, прежде всего, инструмент оптимизации - большинство кода, который работает с reserve
, также должно работать (возможно, немного медленнее) без вызова reserve
. Единственное исключение состоит в том, что reserve
может гарантировать, что итераторы остаются в силе, когда они не будут без вызова для резервирования.
Ответ 3
capacity
- это длина фактического буфера, но этот буфер является приватным для строки; другими словами, это не ваш доступ. std::string
стандартной библиотеки может выделять больше памяти, чем требуется для хранения фактических символов строки. Емкость представляет собой общую выделенную длину. Однако доступ к символам вне s.begin()
и s.end()
по-прежнему является незаконным.
Вы вызываете reserve
в случаях, когда вы ожидаете изменения размера строки, чтобы избежать ненужных перераспределений. Например, если вы планируете конкатенировать десять 20-символьных строк в цикле, может иметь смысл зарезервировать 201 символ (дополнительный для нулевого терминатора) для вашей строки, а не расширять его несколько раз с его размера по умолчанию.
Ответ 4
reserve(n)
действительно выделяет достаточное количество хранилища для хранения как минимум элементов n
, но фактически не заполняет контейнер никакими элементами. Строка все еще пуста (имеет размер 0), но вам гарантировано, что вы можете добавить (например, через push_back
или insert
) не менее n
элементы до того, как внутренний буфер строки должен быть перераспределен, тогда как resize(n)
действительно изменяет размер строки, содержащей элементы n
(и удаляет или добавляет новые элементы, если это необходимо).
Итак, reserve
на самом деле является просто средством оптимизации, когда вы знаете, что добавляете в контейнер кучу элементов (например, в цикле push_back
) и не хотите, чтобы он слишком часто перераспределял хранилище, что расходы на распределение памяти и копирование. Но это не изменяет внешний вид/клиентский вид строки. Он по-прежнему остается пустым (или сохраняет значение текущего элемента).
Аналогично capacity
возвращает количество элементов, которые может содержать строка, пока не потребуется перераспределить его внутреннее хранилище, тогда как size
(и для строки также length
) возвращает фактическое количество элементов в строке.
Ответ 5
Просто потому, что reserve
выделяет дополнительное пространство, это не значит, что вам доступно доступ к нему.
В вашем примере используйте либо resize
, либо перепишите его примерно так:
string my_string;
// I want my string to have 20 bytes long buffer
my_string.reserve( 20 );
int i = 0;
for ( parsing_something_else_loop )
{
char ch = <business_logic>;
// store the character in
my_string += ch;
}
Ответ 6
std::vector вместо std::string также может быть решением - если нет никаких требований к нему.
vector<char> v; // empty vector
vector<char> v(10); // vector with space for 10 elements, here char's
ваш пример:
vector<char> my_string(20);
int i=0;
for ( parsing_something_else_loop )
{
char ch = <business_logic>;
my_string[i++] = ch;
}