инициализация std :: string из char * без копирования

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

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

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

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

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

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

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

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

12.12.2008 00:11:01
6 ОТВЕТОВ
РЕШЕНИЕ

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

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

18
27.08.2013 14:23:54
Ваше решение звучит как то, что я после, хотя, когда вы говорите «вы можете просто использовать ту же самую строку C ++», вы подразумеваете использование clear (), а затем продолжаете строить следующую строку?
Akusete 12.12.2008 02:47:20
На данный момент это простое решение для меня, хотя я не уверен, действительно ли повторное использование определено стандартом (хотя, похоже, оно работает для моей реализации)
Akusete 12.12.2008 03:01:17
Использование clear () должно работать. Насколько я знаю, clear () не освобождает память, используемую строкой, и, следовательно, не влияет на пространство, выделенное Reserve ().
e.James 12.12.2008 08:30:43
@Akusete @ e.James: (Да, я знаю, что это через 2,5 года). Когда я читаю соответствующие части стандарта, clear () определяется в терминах erase (), а erase (), в частности, не определяет, является ли блок используемая память после вызова erase () такая же, как и используемая до вызова erase (), и при этом она не диктует, что емкость () остается неизменной. Смотри, например, 21.3.3 и 21.3.5.5. Разумные реализации, безусловно, могут функционировать таким образом, но убедитесь, что рассматриваете это только как полезную оптимизацию, а не обязательное условие для правильного функционирования программы.
Nicholas Knight 13.05.2011 15:08:59
@ Николас Найт: Честная точка зрения. Как всегда, стандартной процедурой является оптимизация только после проведения измерений и проверки того, что это необходимо. Интересно, знал ли я это еще в 08 году? :)
e.James 16.05.2011 06:17:59

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

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

1
12.12.2008 00:20:32

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

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

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

7
18.10.2011 17:41:36

Вы не можете создать 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.

23
30.11.2016 18:01:51
Я думаю, что ваше решение - лучший подход (изменение кода обработки), к сожалению, в этой ситуации это не вариант.
Akusete 12.12.2008 02:54:42
Существует ли стандартный указанный способ для достижения повторного использования буфера? Я просто не хочу полагаться на реализацию на конкретной платформе.
Akusete 12.12.2008 02:56:26
Если этот код обработки не является библиотечной функцией, которая не использует итераторы или строки, а просто старый char*+ размер.
SasQ 11.06.2015 06:48:12

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

0
12.12.2008 00:53:28
Да, это так, хотя входные данные (C char *) и выходные данные (std :: string) не находятся под моим контролем.
Akusete 12.12.2008 02:35:06

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

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

class lightweight_string {};

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

4
13.12.2008 21:23:49