Подтвердить что ты не робот

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

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

  • постройте большую строку, добавив много меньших (C char *) строк
  • обрезать строку
  • преобразовать строку в С++ const std::string для обработки (только для чтения)
  • повторить

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

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

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

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

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

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

4b9b3361

Ответ 1

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

Подробнее о функции reserve см. эту ссылку.

Ответ 2

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

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

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


UPDATE (так как я все еще вижу случайные варианты ответа на этот ответ):

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

Ответ 3

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

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

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

Ответ 4

Это ответ на боковое мышление, а не прямое решение вопроса, а "размышление" вокруг него. Может быть полезно, может не...

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

класс lightweight_string {};

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

Ответ 5

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

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

Ответ 6

В этом случае лучше обработать char *, а не назначать его std::string.