Std::Strings's Capacity(), Reserve() & Resize() Functions

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.

std::string shrink_to_fit corrupts string

reserve() sets the string's capacity, not its size. Two different things. The capacity is how much memory has been allocated to hold characters. The size is how many characters inside of that allocated memory are actually valid.

shrink_to_fit() shrinks the capacity to match the current size. But your string's size is always 0, so the string is effectively empty, not corrupted, whether you call shrink_to_fit() or not. Printing a std::string prints characters up to its size, not to its capacity.

You need to use resize() instead of reserve(), eg:

std::string buf;
buf.resize(100);
//example of the function that can write to a buffer with 10-100 characters.
strcpy(&buf[0], "Hello");
buf.resize(strlen(buf.c_str()));
buf.shrink_to_fit();
std::cout << buf << std::endl;

That being said, shrink_to_fit() is not required to do anything, it is implementation-defined. You might consider using a separate buffer to read characters into, and then construct your std::string from that buffer, eg:

std::array<char, 100> buf;
//example of the function that can write to a buffer with 10-100 characters.
strcpy(buf.data(), "Hello");
std::string str(buf.data(), strlen(buf.data()));
std::cout << str << std::endl;

Or, in C++17 and later, you can use std::string_view instead, eg:

std::array<char, 100> buf;
//example of the function that can write to a buffer with 10-100 characters.
strcpy(buf.data(), "Hello");
std::string_view sv(buf.data(), strlen(buf.data()));
std::cout << sv << std::endl;

std::string capacity remains same after deleting elements, so is it holding up some memory?

1) Is some memory being held up because of capacity? What if i have not
explicitly reserve()'d?

Even if you did not call reserve, the std::string object can still hold up some memory1 for (even a default constructed) std::string. And this is true with std::string implementations that uses Short String Optimization

2)If i use std::reserve() and dont end up using the entire capcity
then am i wasting memory?

It depends on what you mean by wasting memory; std::string dynamically resizes its buffer to accommodate changes in the size of the string. Well, in the case of Short String Optimized std::string implementations, there is nothing you can do about it. The memory is in the string object itself.

3)Will this extra memory which i am not using be allocated to
something else, if required?

No. A std::string object manages the memory it allocated, and it may or may not give it up2 wholly or partly, until its destroyed. See David Schwartz's comment

EDIT: Suppose i have

string a= "something";

Now i edit the string and know that a won't have more than two
characters.So is it wise to call a.reserve(2) so that memory is not
wasted?

If you modified a in a way that changes a.size(), such as calling resize method or assigning it to a new string of length 2, then the proceeding reserve call can2 be beneficial.

Note that, calling reserve would not reduce the string's contents. std::string::reserve is not permitted to truncate the string. It is only permitted to work on unused memory. If you call std::string::reserve(new_capacity_intended) with new_capacity_intended less than the size() of the string, the best that could possibly happen is the same effect of std::string::shrink_to_fit.

To reduce the string's memory (if the implementation does a binding shrink_to_fit request) and shrink it to the first two characters:

string a= "something";
//resize it first
a.resize(2); //or by some assignment such as a = "so";

//then
a.reserve(2); // or better still a.shrink_to_fit();

1: by memory, I assume Virtual Memory

2: std::string::reserve or std::string::shrink_to_fit may or may not give up the string's unused memory.

Does string::capacity/reserve() count terminating null?

The standard states that:

In all cases, size() <= capacity().

And size() does not include the terminating null.

Since it is possible that size() equals capacity(), in that case it would mean that capacity() also does not count the terminating null.

Note that in C++11 and later, mystring.c_str() is equivalent to mystring.data() is equivalent to &mystring[0], and mystring[mystring.size()] is guaranteed to be '\0'.

Check this Demo.

std::vector resize(0) or clear() - but keep it's capacity

Actually the clear member function keeps the vector capacity unchanged. It only destroys (calls the destructor) each of the vector elements and sets the vector size to 0.

In this situation, at each iteration, I would call clear() to destroy all the vector elements, then call the member function reserve(size) which, in the case where the vector capacity is too small, will increase it to at least size.

C++ How to preserve string capacity when copying

The capacity is not requested to be copied to be same when std::string being copied.

$21.3.1.2/2 basic_string constructors and assignment operators
[string.cons]:

Table 49 — basic_string(const basic_string&) effects
Element Value
data() points at the first element of an allocated copy of the array whose first element is pointed at by str.data()
size() str.size()
capacity() a value at least as large as size()

The only guarantee is the capacity of the string will be at least as large as its size after copying.

It means you have to do this by yourself:

for (int i = myVec.size(); i--;) {
myVec[i].reserve(myVec[i].size() + MAX_BUFFER);
clone.push_back(myVec[i]);
clone.back().reserve(myVec[i].capacity());
}

What is better: reserve vector capacity, preallocate to size or push back in loop?

Better performance will be reached when avoiding dynamic reallocation, so try to have the vector memory be big enough to receive all elements.

Your first solution will be more efficient because if nSize is bigger than default vector capacity, the second one will need a reallocation to be able to store all elements.

As commented by Melkon, reserve is even better:

void myfnc_1(void *a_src, uint32_t a_segment) {
size_t nSize = GetSize();
std::vector<std::string> values;
values.reserve( nSize );
char* v = a_src;

for (size_t i = 0; i < nSize; ++i) {
values.push_back( std::string( v, a_segment ) );
v += a_segment;
}
}

vectors resize(), push.back(), reserve() methods

vector<int> a_vector( 10 );
// equal vector<int> a_vector( 10,0 ); ?

Yes, it's implicitly vector<int> a_vector(10, int());.

cout << "value of vector first " << a_vector.at(0) << endl; //LEGAL
// cout << a_vector.at(10); // ILLEGAL

Not illegal, it will just throw an exception.

a_vector.resize( 12 );
// also does it mean a_vector[10] = 0; and a_vector[11] = 0;?

Yes. The new elements are default constructed.

for (int i = 0; i < 2; i++)
{
//doesn't it same as a_vector.resize( 12 ); now ?
//so why do we need resize(); ?
//also do I need reserve() for using push_back() like this ?
a_vector.push_back(0);
}

Yes, in this case it's the same. Calling resize is shorter.


In conclusion:

  • resize either removes or adds default constructed values in order to make the size of the container that one
  • reserve has more to do with the capacity of the container, which is the internal size of the dynamically allocated array

What does the default allocator do when a std::vector is resized (via either reserve() or resize())?

C++ allocators do not support anything like C's realloc. Whenever vector needs more memory, it has to allocate new storage, move from old to new, and deallocate the old.

Either way, realloc wouldn't suit vector. With typical allocators, realloc will only save you a heavy copy operation if you are shrinking its size, or in some cases growing by only a few bytes. vector doesn't ever shrink, and it only grows in very large steps.

Note that move support is a new behavior in C++ 2011. Previous versions will copy.



Related Topics



Leave a reply



Submit