What Does the Standard Say About How Calling Clear on a Vector Changes the Capacity

What does the standard say about how calling clear on a vector changes the capacity?

Depending on the version of the standard you are looking at,
clear is defined as the equivalent of erase(begin(), end()), or (in C++11):

"Destroys all elements in a. Invalidates all
references, pointers, and iterators referring to
the elements of a and may invalidate the
past-the-end iterator."

In neither case is it allowed to modify
the capacity; the following code is guaranteed safe by the
standard:

std::vector<int> v;
for (int i = 0; i != 5; ++ i) {
v.push_back(i);
}
assert(v.capacity() >= 5);
v.clear();
assert(v.capacity() >= 5);
v.push_back(10);
v.push_back(11);
std::vector<int>::iterator i = v.begin() + 1;
v.push_back(12);
v.push_back(13);
*i = 42; // i must still be valid, because none of
// the push_back would have required an
// increase of capacity

(The reason for the change in wording in C++11: the committee
didn't want to require MoveAssignable for clear, which would
have been the case if it were defined in terms of erase.)

Will a call to std::vector::clear() set std::vector::capacity() to zero?

It is specified that std::vector<T>::clear() affects the size. It might not affect the capacity. For resetting the capacity, use the swap trick:

    std::vector<int> v1;

// somehow increase capacity

std::vector<int>().swap(v1);

Note: Since this old answer is still getting upvotes (thus people read it), I feel the need to add that C++11 has added std::vector<...>::shrink_to_fit(), which requests the vector to remove unused capacity.

Does clearing a vector affect its capacity?

No, it doesn't. The capacity of a vector never decreases. That isn't mandated by the standard but it's so both in standard library implementations of VC++ and g++. In order to set the capacity just enough to fit the size, use the famous swap trick

vector<T>().swap(foo);

In C++11 standard, you can do it more explicitly:

foo.shrink_to_fit();

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.

Do I need to call clear() when I am done with a vector?

No.

Classes in C++ have a destructor that gets called when the class object goes out of scope or is deleted. While you are correct that std::vector dynamically allocates space under the hood, the std::vector destructor will deallocate the memory for you resulting in a happy leak-free program.

From the cppreference page, when the vector destructor is called it...

Destructs the container. The destructors of the elements are called and the used storage is deallocated. Note, that if the elements are pointers, the pointed-to objects are not destroyed.


Also note that from the cppreference on clear, the function...

Leaves the capacity() of the vector unchanged...

So when you call clear the memory isn't even actually being free'd! (see this SO question for more on what clear is actually doing)

Does vector::erase reduce vector::capacity?

Not necessarily no. When reading the C++ standard (and cppreference proxies the standard remarkably well), if something is not explicitly mentioned, then assume such a something is not required.

It would possibly be sub-optimal for a C++ Standard Library implementation to do so.

Calling clear() on a vector does not actually delete data in data()?

The vector's data() method returns a raw pointer to the vector's allocated array in memory. clear() destroys the contents of that array if needed and sets the vector's size() to 0, but does not reallocate the array itself, and thus does not change the vector's capacity(). Calling the vector's shrink_to_fit() method reallocates the array so its capacity() matches its size(), if possible (shrink_to_fit() is advisory only and not guaranteed to actually do anything).

Also, when constructing a std::string from a char* pointer by itself, the char data needs to be null-terminated, but your data is not. You need to push a null terminator into the vector before using data():

void splitString(const string &str, char delimiter, vector<string> * out) {
vector<char> word_buffer;
for (int i = 0; i < str.length(); ++i) {
if (str[i] == delimiter) {
word_buffer.push_back('\0');
out->push_back(word_buffer.data());
word_buffer.clear();
} else {
word_buffer.push_back(str[i]);
}
}
if (!word_buffer.empty()) {
word_buffer.push_back('\0')
out->push_back(word_buffer.data());
}
}

Otherwise, you can simply take the vector's size() into account when constructing the strings, no null terminators needed:

void splitString(const string &str, char delimiter, vector<string> * out) {
vector<char> word_buffer;
for (int i = 0; i < str.length(); ++i) {
if (str[i] == delimiter) {
out->push_back(string(word_buffer.data(), word_buffer.size()));
// alternatively:
// out->emplace_back(word_buffer.data(), word_buffer.size());
word_buffer.clear();
}
else {
word_buffer.push_back(str[i]);
}
}
if (!word_buffer.empty()) {
out->push_back(string(word_buffer.data(), word_buffer.size()));
// alternatively:
// out->emplace_back(word_buffer.data(), word_buffer.size());
}
}

That being said, there are other ways to implement a splitString() function without needing the word_buffer vector at all, eg:

void splitString(const string &str, char delimiter, vector<string> * out) {
string::size_type start = 0, pos = str.find(delimiter);
while (pos != string::npos) {
out->push_back(str.substr(start, pos-start));
start = pos + 1;
pos = str.find(delimiter, start);
}
if (start < str.size()) {
if (start > 0) {
out->push_back(str.substr(start));
} else {
out->push_back(str);
}
}
}

Live Demo

void splitString(const string &str, char delimiter, vector<string> * out) {
istringstream iss(str);
string word;
while (getline(iss, word, delimiter))
out->push_back(std::move(word));
}

Live Demo

But, even if you wanted to buffer the words manually, a std::string would have made more sense than a std::vector<char>, especially since you are outputting std::string values:

void splitString(const string &str, char delimiter, vector<string> * out) {
string word_buffer;
for (string::size_type i = 0; i < str.length(); ++i) {
if (str[i] == delimiter) {
out->push_back(std::move(word_buffer));
word_buffer.clear();
} else {
word_buffer.push_back(str[i]);
}
}
if (!word_buffer.empty()) {
out->push_back(std::move(word_buffer));
}
}

Live Demo

With a 2D vector, does calling clear() on the 1st level clear all the memory associated with that vector?

Since a vector neatly destructs its elements and deallocates the used memory when itself gets destructed, and v.clear() also destructs the elements in the vector, calling v.clear() is a perfectly valid way to delete an entire 2D vector.

Note that if you want also the memory for v itself cleaned up you need to call v.shrink_to_fit() after v.clear().



Related Topics



Leave a reply



Submit