std::strings’s capacity(), reserve() & resize() functions

Isn’t that the point to reserve() size so you can access it?

No, that’s the point of resize().

reserve() only gives to enough room so that future call that leads to increase of the size (e.g. calling push_back()) will be more efficient.

From your use case it looks like you should use .push_back() instead.

my_string.reserve( 20 );

for ( parsing_something_else_loop )
{
    char ch = <business_logic>;
    my_string.push_back(ch);
}

How is it that the string has the capacity but can’t really access it with []?

Calling .reserve() is like blowing up mountains to give you some free land. The amount of free land is the .capacity(). The land is there but that doesn’t mean you can live there. You have to build houses in order to move in. The number of houses is the .size() (= .length()).

Suppose you are building a city, but after building the 50th you found that there is not enough land, so you need to found another place large enough to fit the 51st house, and then migrate the whole population there. This is extremely inefficient. If you knew you need to build 1000 houses up-front, then you can call

my_string.reserve(1000);

to get enough land to build 1000 houses, and then you call

my_string.push_back(ch);

to construct the house with the assignment of ch to this location. The capacity is 1000, but the size is still 1. You may not say

my_string[16] = 'c';

because the house #16 does not exist yet. You may call

my_string.resize(20);

to get houses #0 ~ #19 built in one go, which is why

my_string[i++] = ch;

works fine (as long as 0 ≤ i ≤ 19).

See also http://en.wikipedia.org/wiki/Dynamic_array.


For your add-on question,

.resize() cannot completely replace .reserve(), because (1) you don’t always need to use up all allocated spaces, and (2) default construction + copy assignment is a two-step process, which could take more time than constructing directly (esp. for large objects), i.e.

#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;
}

Will waste you at least 9 seconds to run, while

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;
}

wastes only 6 seconds.

std::string only copies std::vector‘s interface here.

Leave a Comment