web-dev-qa-db-ja.com

コピーなしでchar *からstd :: stringを初期化しています

大量(GB)のデータを次のように処理する必要がある状況があります。

  1. 多くの小さな(C char *)文字列を追加して大きな文字列を作成する
  2. ひもを整える
  3. 文字列を処理のためにC++ const std :: stringに変換します(読み取り専用)
  4. 繰り返す

各反復のデータは独立しています。

私の質問は、現時点で最大のパフォーマンスの問題であるため、ヒープに割り当てられたメモリ使用量を最小化(可能な場合は排除)したいと思います。

データを内部的に割り当て/コピーするためにstd :: stringを必要とせずに、C文字列(char *)をstl C++文字列(std :: string)に変換する方法はありますか?

または、文字列ストリームなどを使用して、大きなバッファーを再利用できますか?

編集:回答に感謝します。明確にするために、改訂された質問は次のようになると思います。

stl C++文字列を(複数の追加を介して)効率的に構築するにはどうすればよいですか。そして、各ループが完全に独立しているループでこのアクションを実行する場合、この割り当てられたスペースをどのように再利用できますか

44
Akusete

手順1でC++文字列を使用することはまったく可能ですか? string::reserve(size_t)を使用する場合は、小さい文字列を追加するときに複数のヒープが割り当てられないように十分な大きさのバッファーを割り当て、残りのすべての手順で同じC++文字列を使用できます。

reserve関数の詳細については、 このリンク を参照してください。

18
e.James

データをコピーせずにstd :: stringを実際に形成することはできません。文字列ストリームはおそらくパスからパスへとメモリを再利用しますが(標準は実際にそうする必要があるかどうかについては黙っていると思いますが)、それでもコピーは避けられません。

この種の問題に対する一般的なアプローチは、ステップ3でデータを処理するコードを記述して、イテレータの開始/終了ペアを使用することです。次に、std :: string、charのベクトル、生のポインタのペアなどのいずれかを簡単に処理できます。std:: stringのようなコンテナタイプを渡すのとは異なり、メモリがどのように割り当てられているかを認識したり、気にしません。それはまだ呼び出し元に属しているためです。このアイデアを論理的な結論に導くのは boost :: range です。これにより、オーバーロードされたすべてのコンストラクターが追加され、呼び出し元が文字列/ベクター/リスト/.begin()を使用して任意の種類のコンテナーを渡すことができます。 .end()、または個別の反復子。

任意のイテレータ範囲で動作するように処理コードを記述したら、カスタムイテレータを書くこともできます(それほど難しくはありません。基本的に、いくつかの標準のtypedefを持つオブジェクトと演算子++/*/=/==/!=オーバーロードされて、前方のみのイテレータを取得します。これにより、作業中のフラグメントの最後に到達するたびに次のフラグメントに進み、空白をスキップします(これは、トリムによって意図されたものだと思います)。ストリング全体を連続して組み立てる必要がまったくなかったこと。これが成功するかどうかは、フラグメントの数/フラグメントの大きさによって異なります。これは本質的に、Martin Yorkによって言及されたSGIロープとは次のとおりです。appendは、連続したバッファーの代わりにフラグメントのリンクされたリストを形成する文字列で、はるかに長い値に適しています。


[〜#〜] update [〜#〜](まだこの回答に賛成投票が時折見られるため):

C++ 17には別の選択肢が導入されています。多くの関数シグネチャでstd :: stringを置き換えた std :: string_view は、文字データへの非所有参照です。 std :: stringから暗黙的に変換できますが、std :: stringによる不必要なコピーを避けて、他の場所に所有されている連続データから明示的に構築することもできます。

23
puetzk

本当に大きな文字列を支援するために、SGIのSTLにはRopeクラスがあります。
非標準ですが、役に立つ場合があります。

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

どうやらロープは標準の次のバージョンにあります:-)
開発者のジョークに注意してください。ロープは大きな紐です。 (ハハ):-)

7
Martin York

これは側面的な思考の答えであり、質問に直接対処するのではなく、それについて「考える」ことです。役に立つかもしれませんが、役に立たないかもしれません...

Std :: stringの読み取り専用処理は、std :: stringの機能の非常に複雑なサブセットを実際には必要としません。 std :: stringsですべての処理を実行するコードを検索/置換して、代わりに他のタイプを使用する可能性はありますか?空のクラスから始めます:

クラスLightweight_string {};

次に、すべてのstd :: string参照をLightweight_stringに置き換えます。コンパイルを実行して、軽量ストリングがドロップイン置換として機能するために必要な操作を正確に見つけます。その後、必要に応じて実装を機能させることができます。

4

各反復は、各反復に同じstd :: stringを使用できるほど独立していますか? std :: stringの実装が、以前に別の目的で使用されていたconst char *を割り当てた場合に、メモリを再利用できるほどスマートであることを願っています。

Char *をstd :: stringに割り当てると、常に少なくともデータをコピーする必要があります。メモリ管理はstd :: stringを使用する主な理由の1つなので、オーバーライドすることはできません。

1
David Norman

この場合、char *をstd :: stringに割り当てるのではなく、直接処理する方がよいでしょう。

0
Alan