инициализация std :: string из char * без копирования - PullRequest
43 голосов
/ 12 декабря 2008

У меня есть ситуация, когда мне нужно обрабатывать большие (многие ГБ) объемы данных как таковые:

  1. создать большую строку, добавив множество небольших (C char *) строк
  2. обрезать строку
  3. преобразовать строку в C ++ const std :: string для обработки (только для чтения)
  4. повтор

Данные в каждой итерации независимы.

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

Есть ли способ преобразовать строку C (char *) в строку stl C ++ (std :: string), не требуя std :: string для внутреннего размещения / копирования данных?

В качестве альтернативы, можно ли использовать строковые потоки или что-то подобное для повторного использования большого буфера?

Редактировать: Спасибо за ответы, для ясности, я думаю, что пересмотренный вопрос будет:

Как эффективно построить (через несколько добавлений) строку stl C ++. И если я выполняю это действие в цикле, где каждый цикл полностью независим, как я могу повторно использовать это выделенное пространство.

Ответы [ 6 ]

22 голосов
/ 12 декабря 2008

На самом деле вы не можете сформировать std :: string без копирования данных. Поток строк, вероятно, будет повторно использовать память от прохода к проходу (хотя я думаю, что стандарт ничего не говорит о том, должен ли он на самом деле), но он все равно не избежит копирования.

Распространенным подходом к решению этой проблемы является написание кода, который обрабатывает данные на шаге 3 для использования пары итератор начала / конца; затем он может легко обрабатывать либо std :: string, вектор символов, пару необработанных указателей и т. д. В отличие от передачи ему типа контейнера, такого как std :: string, он больше не будет знать или заботиться о том, как распределяется память, так как это все равно будет принадлежать звонящему. Это логическое завершение этой идеи: boost :: range , которая добавляет все перегруженные конструкторы, чтобы вызывающая сторона просто передавала строку / вектор / список / любой вид контейнера с помощью .begin () и. end () или отдельные итераторы.

Написав свой код обработки для работы с произвольным диапазоном итераторов, вы могли бы даже написать собственный итератор (не такой сложный, как кажется, просто объект с некоторыми стандартными определениями типов и оператором ++ / * / = / == /! = перегружен, чтобы получить итератор только для пересылки), который заботится о переходе к следующему фрагменту каждый раз, когда он попадает в конец того фрагмента, над которым он работает, пропуская пропуски (я полагаю, это то, что вы имели в виду под trim). То, что вам никогда не приходилось собирать всю цепочку подряд. Будет ли это победой, зависит от того, сколько фрагментов / сколько у вас фрагментов. По сути, это то, о чем говорил Мартин Йорк по веревке SGI: строка, в которой append образует связанный список фрагментов, а не непрерывный буфер, который, таким образом, подходит для гораздо более длинных значений.


ОБНОВЛЕНИЕ (поскольку я все еще вижу случайные отзывы об этом ответе):

C ++ 17 вводит другой выбор: std :: string_view , который заменил std :: string во многих сигнатурах функций, является несобственной ссылкой на символьные данные. Он неявно конвертируется из std :: string, но также может быть явно создан из смежных данных, принадлежащих где-то еще, избегая ненужного копирования, налагаемого std :: string.

18 голосов
/ 12 декабря 2008

Можно ли вообще использовать строку C ++ на шаге 1? Если вы используете string::reserve(size_t), вы можете выделить достаточно большой буфер для предотвращения многократного выделения кучи при добавлении меньших строк, а затем вы можете просто использовать эту же строку C ++ на всех оставшихся шагах.

См. эту ссылку для получения дополнительной информации о функции reserve.

7 голосов
/ 12 декабря 2008

Чтобы помочь с действительно большими строками, SGI имеет класс Rope в своем STL.
Нестандартный, но может быть полезным.

http://www.sgi.com/tech/stl/Rope.html

Видимо веревка в следующей версии стандарта: -)
Обратите внимание на шутку разработчиков. Веревка - это большая нить. (Ха Ха): -)

4 голосов
/ 14 декабря 2008

Это ответ с латеральным мышлением, не связанный непосредственно с вопросом, а «размышляющий» вокруг него. Может быть полезно, а может и нет ...

Обработка только для чтения std :: string на самом деле не требует очень сложного подмножества функций std :: string. Есть ли вероятность, что вы могли бы выполнять поиск / замену в коде, который выполняет всю обработку std :: strings, поэтому вместо него требуется какой-то другой тип? Начните с пустого класса:

class lightweight_string {};

Затем замените все ссылки std :: string на lightweight_string. Выполните компиляцию, чтобы выяснить, какие именно операции необходимы для lightweight_string, чтобы он выполнял функцию замены. Тогда вы можете заставить свою реализацию работать так, как вы хотите.

1 голос
/ 12 декабря 2008

Достаточно ли независима каждая итерация, чтобы вы могли использовать одну и ту же строку std :: string для каждой итерации? Можно было бы надеяться, что ваша реализация std :: string достаточно умна, чтобы повторно использовать память, если вы назначите ей const char *, когда она ранее использовалась для чего-то другого.

Назначение char * в std :: string всегда должно как минимум копировать данные. Управление памятью является одной из основных причин использования std :: string, поэтому вы не сможете ее переопределить.

0 голосов
/ 12 декабря 2008

В этом случае, может быть, лучше обработать символ * напрямую, а не назначать его в std :: string.

...