Функции std :: strings (), reserve () и resize () - PullRequest
19 голосов
/ 01 марта 2012

Я хочу использовать std :: string просто для создания динамического буфера, а затем перебирать его, используя индекс.Является ли resize () единственной функцией, которая фактически выделяет буфер?

Я пытался сделать резерв (), но когда я пытаюсь получить доступ к строке через индекс, он утверждает.Также, когда емкость строки по умолчанию кажется 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 (), то это работает нормально.Как получается, что строка имеет емкость, но не может получить к ней доступ с помощью []?Разве это не точка для резерва () размера, чтобы вы могли получить к нему доступ?

Надстройка В ответ на ответы я хотел бы спросить людей, почему кто-то используетReserve (), когда resize () делает то же самое и инициализирует строку?Я должен сказать, что не очень ценю аргумент производительности в этом случае.Все, что resize () делает дополнительно к тому, что делает reserve (), - это просто инициализирует буфер, который, как мы знаем, всегда приятно делать в любом случае.Можем ли мы проголосовать за резерв () с острова?

Ответы [ 6 ]

33 голосов
/ 01 марта 2012

Разве это не точка для резерва () размера, чтобы вы могли получить к нему доступ?

Нет, это точка 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 ≤ i ≤ 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 здесь.

5 голосов
/ 01 марта 2012

Нет - смысл 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 может гарантировать, что итераторы остаются действительными, когда они не обходятся без запроса на резервирование.

4 голосов
/ 01 марта 2012

capacity - это длина фактического буфера, но этот буфер private для строки; другими словами, это не ваш доступ. std::string стандартной библиотеки может выделить больше памяти, чем требуется для хранения фактических символов строки. Емкость - это общая выделенная длина. Однако доступ к символам за пределами s.begin() и s.end() по-прежнему запрещен.

Вы вызываете reserve в тех случаях, когда вы ожидаете изменения размера строки, чтобы избежать ненужных перераспределений. Например, если вы планируете объединить десять 20-символьных строк в цикле, может иметь смысл зарезервировать 201 символ (дополнительный для нулевого терминатора) для вашей строки, а не расширять ее несколько раз от размера по умолчанию. .

2 голосов
/ 01 марта 2012

reserve(n) действительно выделяет достаточно памяти для хранения по крайней мере n элементов, но на самом деле он не заполняет контейнер какими-либо элементами. Строка по-прежнему пуста (имеет размер 0), но вам гарантировано, что вы можете добавить (например, через push_back или insert) как минимум n элементов, прежде чем потребуется перераспределить внутренний буфер строки, тогда как resize(n) действительно изменяет размер строки, чтобы содержать n элементов (и удаляет или добавляет новые элементы в случае необходимости).

Таким образом, reserve на самом деле является простым средством оптимизации, когда вы знаете, что добавляете кучу элементов в контейнер (например, в цикле push_back) и не хотите, чтобы он слишком часто перераспределял хранилище, что Расходы на выделение памяти и копирование. Но это не меняет внешний / клиентский вид строки. Он все еще остается пустым (или сохраняет текущий счетчик элементов).

Аналогичным образом capacity возвращает количество элементов, которое может содержать строка, до тех пор, пока ей не потребуется перераспределить внутреннее хранилище, тогда как size (и для строки также length) возвращает фактическое количество элементов в строке.

1 голос
/ 01 марта 2012

То, что 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;
}
0 голосов
/ 01 марта 2012

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;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...