web-dev-qa-db-ja.com

std :: string capacity()、reserve()およびresize()関数

Std :: stringを使用して動的バッファを作成し、インデックスを使用して反復処理するだけです。 resize()は実際にバッファを割り当てる唯一の関数ですか?

私はreserve()を試しましたが、インデックスを介して文字列にアクセスしようとすると、アサートします。また、文字列のデフォルトの容量が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
}

Reserve()の代わりにresize()を実行すると、正常に機能します。文字列には容量がありますが、実際に[]でアクセスできないのはなぜですか?アクセスできるように、reserve()サイズを指定することがポイントではありませんか?

アドオン答えに応えて、私はstlの人々に尋ねたいのですが、なぜresize()がまったく同じで、文字列も初期化するときに、誰かがreserve()を使用するのですか?この場合のパフォーマンスの議論にはそれほど感謝しません。 resize()が他に行うのは、reserve()が行うことのほかに、バッファを初期化するだけです。島外でreserve()に投票できますか?

20
zar

アクセスできるように、reserve()サイズを指定することがポイントではありませんか?

いいえ、それが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)デフォルトの構築+コピー割り当ては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_のインターフェースのみをコピーします。

33
kennytm

いいえ-reserveの目的は、再割り当てを防ぐことです。 resizeは使用可能なサイズを設定しますが、reserveは設定しません-予約されているがまだ直接使用できない容量を設定するだけです。

以下に1つの例を示します。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。その1つの例外は、reserveを使用すると、reservationを呼び出さないとイテレータが有効でない場合でも、イテレータが有効なままであることを保証できます。

5
Jerry Coffin

reserve(n)は確かに、少なくともn要素を保持するのに十分なストレージを割り当てますが、実際にはコンテナを要素で満たしません。文字列はまだ空です(サイズは0です)が、文字列の内部バッファの前に少なくともinsert要素を(たとえば_Push_back_またはnを介して)追加できることが保証されますresize(n)は実際に文字列のサイズを変更してn要素を含める必要があります(必要に応じて新しい要素を削除または追加します)。

したがって、reserveは実際には単なる最適化機能であり、コンテナに多数の要素を追加していることがわかっていて(たとえば、_Push_back_ループ内)、ストレージを頻繁に再割り当てしたくない場合、メモリの割り当てとコピーのコストが発生します。ただし、文字列の外部/クライアントビューは変更されません。それはまだ空のままです(または現在の要素数を保持します)。

同様に、capacityは、内部ストレージを再割り当てする必要があるまで文字列が保持できる要素の数を返しますが、size(および文字列の場合はlength)は実際の要素の数を返します文字列内。

2
Christian Rau

std :: stringの代わりにstd :: vectorも解決策になる可能性があります-要件がない場合。

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;
}
0
Jörg Beyer