Memory deallocation in std::vector - c++

I'm trying to figure out how the memory management for std::vector works when you remove elements. It is my understanding that once memory was allocated for the vector, no pop_back\erase action would reduce the capacity of the vector, and the only way to do so is using shrink_to_fit which was introduced in C++ 11. Please correct me if I'm wrong.

You are correct, mostly.
There are only a few ways to reduce a std::vector's capacity…
Move Assignment
#include <iostream>
#include <vector>
int
main()
{
std::vector<int> v1(10);
std::vector<int> v2(20);
std::cout << "v2.capacity() = " << v2.capacity() << '\n';
std::cout << "Move assign v2 with less capacity\n";
v2 = std::move(v1);
std::cout << "v2.capacity() = " << v2.capacity() << '\n';
}
which should output:
v2.capacity() = 20
Move assign v2 with less capacity
v2.capacity() = 10
If the allocator involved is set a certain way, diminished capacity is not assured. But in the common case as shown above, capacity will shrink. If you really want to know about the nitty gritty details of how capacity might not shrink during move assignment, ask another question, and I can dig into that in detail.
shrink_to_fit
#include <iostream>
#include <vector>
int
main()
{
std::vector<int> v(20);
std::cout << "v.capacity() = " << v.capacity() << '\n';
std::cout << "erasing...\n";
v.erase(v.begin() + v.size()/2, v.end());
std::cout << "v.capacity() = " << v.capacity() << '\n';
std::cout << "shrink_to_fit...\n";
v.shrink_to_fit();
std::cout << "v.capacity() = " << v.capacity() << '\n';
}
which will probably output:
v.capacity() = 20
erasing...
v.capacity() = 20
shrink_to_fit...
v.capacity() = 10
But it is allowed to output:
v.capacity() = 20
erasing...
v.capacity() = 20
shrink_to_fit...
v.capacity() = 20
swap
#include <iostream>
#include <vector>
int
main()
{
std::vector<int> v(20);
std::vector<int> v2(10);
std::cout << "v.capacity() = " << v.capacity() << '\n';
std::cout << "swap...\n";
swap(v, v2);
std::cout << "v.capacity() = " << v.capacity() << '\n';
}
which should output:
v.capacity() = 20
swap...
v.capacity() = 10
I know of no other ways to shrink the capacity of a vector (besides obviously equivalent constructs such as v.swap(v2)).
A good rule of thumb is that the capacity of a vector never shrinks unless:
It transfers resources with another vector (e.g. move assignment or swap).
shrink_to_fit is called.
Oh, and I nearly forgot a third possibility: any time you move from a vector, the moved-from vector might have a diminished capacity (or it might not). It is unspecified, so you can't count on it. In general moved-from values should be variables where you don't care what their current state is. You will subsequently destruct them or assign them a new known state (assignment doesn't have to be via the assignment operator; could be clear() followed by push_back() for example).

Related

Confused with c++ swap function: std::vector<int>().swap(search_indices);

Here is the code, I am very confused. swap function is usually used to exchange the value of two parameters, like a.swap(b) or swap(a, b). What is the meaning of swap here?
std::vector<int> search_indices;
std::vector<float> distances;
int keypointNum = 0;
do
{
keypointNum++;
std::vector<int>().swap(search_indices);
std::vector<float>().swap(distances);
int id;
iterUnseg = unVisitedPtId.begin();
id = *iterUnseg;
indices->indices.push_back(features[id].ptId);
unVisitedPtId.erase(id);
tree.radiusSearch(features[id].pt, _curvature_non_max_radius, search_indices, distances);
for (int i = 0; i < search_indices.size(); ++i)
{
unVisitedPtId.erase(search_indices[i]);
}
} while (!unVisitedPtId.empty());
I have looked for how swap function works, no related explanations.
Given std::vector<int> v; definition, std::vector<int>().swap(v); clears vector v and disposes of the memory it reserved (so that v.capacity() returns 0). Starting from C++11, an arguably better way to write it is:
v.clear();
v.shrink_to_fit();
It is a trick to clear a vector and free all the allocated memory for its elements.
In these statements
std::vector<int>().swap(search_indices);
std::vector<float>().swap(distances);
there are used empty temporary created vectors, std::vector<int>() and std::vector<float>(), that are swapped with the vectors search_indices and distances.
After the calls of the member function swap the both vectors search_indices and distances become empty. In turn the temporary vectors that after the swapping contain the elements of the above two vectors will be destroyed.
This trick is used because if you will just write
search_indices.clear();
distances.clear();
the allocated memory can be preserved. That is the member function capacity can return a non-zero value.
Here is a demonstration program.
#include <iostream>
#include <vector>
int main()
{
std::vector<int> v = { 1, 2, 3, 4, 5 };
std::cout << "v.size() = " << v.size() << '\n';
std::cout << "v.capacity() = " << v.capacity() << '\n';
std::cout << '\n';
v.clear();
std::cout << "v.size() = " << v.size() << '\n';
std::cout << "v.capacity() = " << v.capacity() << '\n';
std::cout << '\n';
std::vector<int>().swap( v );
std::cout << "v.size() = " << v.size() << '\n';
std::cout << "v.capacity() = " << v.capacity() << '\n';
}
The program output is
v.size() = 5
v.capacity() = 5
v.size() = 0
v.capacity() = 5
v.size() = 0
v.capacity() = 0
As you can see after calling the member function swap with the temporary empty vector the capacity of the vector v becomes equal tp 0.
To get the same effect using the method clear you should after calling it also to call the method shrink_to_fit(). For example
v.clear();
v.shrink_to_fit();
It seems that this is a strategy to free up memory. I wrote a test code here:
#include <iostream>
#include <vector>
using namespace std;
int main()
{
std::vector<int> test(9, 0);
std::cout <<test.size() << std::endl;
std::vector<int>().swap(test);
std::cout <<test.size() << std::endl;
cout<<"Hello World";
return 0;
}
The output is:
9
0
Hello World

What is the behaviour of vector<T>().swap(x)?

I'm currently trying to re-implement the vector container. I just read on this website that vector<T>().swap(x);, where x is a vector previously declared, could be used to clear and reallocate the x vector to 0.
I realized with this example that I'm not familiar with the use of a constructor (or what I think is a constructor).
#include <iostream>
#include <string>
#include <vector>
typedef std::string T;
void display(const std::vector<T>& input) {
std::cout << "------START------" << std::endl;
for (int i = 0; i < input.size(); i++)
std::cout << input[i] << std::endl;
std::cout << "------END-------" << std::endl;
std::cout << "input size: " << input.size() << std::endl;
std::cout << "input capacity: " <<input.capacity() << std::endl;
}
int main() {
std::vector<T> src(1, "Matthieu0");
std::vector<T> empty;
src.push_back("Guillaume1");
src.push_back("Paul2");
src.push_back("Julien3");
std::vector<T> tmp(src);
std::cout << "------swap1------" << std::endl;
display(empty);
empty.swap(tmp);
display(empty);
std::cout << "------swap2------" << std::endl;
display(src);
std::vector<T>().swap(src);
display(src);
return 0;
}
So, I have no problem understanding swap1 behavior, crystal clear. But I don't understand what is happening under the hood with swap2 and how I could implement it.
But I don't understand what is happening under the hood with swap2
When you wrote
//-vvvvvvvvvvvvvvvv------------->a temporary std::vector
std::vector<T>().swap(src); //this calls swap on the temporary(unnamed) std::vector
In the above statement you're calling std::vector::swap on the temporary(unnamed) std::vector created from the expression std::vector<T>().
This is similar to the swap1 that you did with the exception that in swap1 you were calling std::vector::swap on a named object empty but here you're calling it on the temporary object.
Many developers want to release memory occupied by a vector after std::vector::resize(0).
It can be done since C++11 by calling std::vector::shrink_to_fit(). There is no such method until C++11, in C++98, and developers use such a trick:
Create an empty vector.
Swap contents of vectors.
Destroy the vector created on the step 1.
For performing the step 3 as much earlier as possible, the limited scope block { ... } is used.
std::vector<int> v(10);
std::cout << v.size(); << std::end; // prints 10
{
std::vector<int> tmp;
std::cout << tmp.size(); << std::end; // prints 0
v.swap(tmp);
std::cout << tmp.size(); << std::end; // prints 10
}
std::cout << v.size(); << std::end; // prints 0
It can be shorter if an unnamed temporary vector generated by a compiler is used. The life time of an unnamed temporary variable is well known:
std::vector<int> v(10);
std::cout << v.size(); << std::end; // prints 10
v.swap(std::vector<int>());
std::cout << v.size(); << std::end; // prints 0
std::vector<int> v(10);
std::cout << v.size(); << std::end; // prints 10
std::vector<int>().swap(v);
std::cout << v.size(); << std::end; // prints 0
It can be still used since C++11 for making
v.resize(0);
v.shrink_to_fit();
shorter and to guarantee freeing memory:
v.swap({});

C++ - How are multi-dimensional vectors stored?

I have two questions with respect to vectors.
Let us say I have a multi-dimensional vector as follows :-
vector< vector<int> > A;
Then A[0], A[1], etc are vectors. How are the vectors stored in A? I
mean what information about the vectors A[0] and A[1] is stored in A?
And does memory re-allocation of the individual vectors such as A[2]
cause re-allocation of A as well?
Second, I tried to see how the addresses of a vector change with re-allocation. I used the following code:-
Code:
vector<int> A;
int* x ;
int* y ;
vector<int>* ad;
vector<int>* bd;
for(int i = 0 ; i < 10000; i++){
A.push_back(i);
if(i == 2){
y = &A[0];
ad = &A;
}
x = &A[0];
bd = &A;
}
I found that, the address for A does not change even though the address for A[0] changes. This is to be expected since vectors work in the background by making use of new and delete. But my question is how much information (or which information) about the vector is stored in the address &A (considering the address of &A does not change ). This is the question I have with respect to the first question as well.
I am trying to better understand how vectors work by default.
how much information (or which information) about the vector is stored in the address &A
You are correct in assuming that the data for the vector is stored separately from the vector object itself - typically, in dynamic memory.
The three things the vector object itself needs to know are
The location of the vector's data - we need this to perform the [] operator,
The size currently allocated - we need this to know when to grow the array, and
The number of elements actually placed into the vector - we need this to know where to push_back, and what to return from size().
Different implementations are possible, storing as little as a single pointer in the vector object itself. However, a typical implementation stores a pointer to the beginning of the allocated block, a pointer to the end of the active part of the allocated block, and a pointer to the end of the allocated block.
Regarding the vector's address: The address of A does not change, not because A is a vector, but because no variable's address changes while the function where you define it (or rather, the specific call of your function) is executing. I think you may be confusing A's address (ad, bd in your example) with the address of what A uses to store the elements of the vector (x and y, essentially, in your example). Vectors allocate, de-allocate or re-allocate storage.
Note that A[0] is not a variable that you defined. It is the result of the invocation of A.operator[]; so its location can change.
Regarding what's actually stored at &A: That is kind of complicated. You will need to look at the header file vector within your C++ installation. Or perhaps it would be better to have a look at the webpage for std::vector at cppreference.com. Note there's a lot of templates, and some subclassing, and some explicit template specializations, so like I said - complicated. You might want reconsider whether you really want to look under the hood just to understand how this container works as a general rule, or whether the class' public methods and sizeof() figure are sufficient for now.
I am a beginner in c++ and STL, so I just test your problem with some simple codes;
first, i have these codes:
std::vector<int> tmp;
std::cout << sizeof(tmp) << " " << tmp.size() << " " << tmp.capacity << std::endl;
the output is:
12 0 0
then, we insert some values into the vector
for(int i = 0; i != 10; ++i) tmp.push_back(i);
std::cout << sizeof(tmp) << " " << tmp.size() << " " << tmp.capacity << std::endl;
the output is
12 10 16
then, we can draw a conclusion that the vector just keep a pointer, so the sizeof() result didn't changed.
So, the answer of your question is , the child vector's push_back will not result in the reallocation of the parent vector(i don't know how to express the role of these two vectors).
There are some simple codes:
std::vector<int> v1(10);
std::vector<int> v2(10);
int i;
for(i = 0; i != 10; ++i)
v1[i] = i;
for(i = 0; i != 10; ++i)
v2[i] = i;
vv.push_back(v1);
vv.push_back(v2);
std::cout << "v1 capacity: " << v1.capacity() << " v1 size: " << v1.size() << std::endl;
std::cout << "v2 capacity: " << v2.capacity() << " v2 size: " << v2.size() << std::endl;
std::cout << "vv capacity: " << vv.capacity() << " vv size: " << vv.size() << std::endl;
for(i = 10; i != 20; ++i)
v1.push_back(i);
for(i = 10; i != 20; ++i)
v2.push_back(i);
std::cout << "v1 capacity: " << v1.capacity() << " v1 size: " << v1.size() << std::endl;
std::cout << "v2 capacity: " << v2.capacity() << " v2 size: " << v2.size() << std::endl;
std::cout << "vv capacity: " << vv.capacity() << " vv size: " << vv.size() << std::endl;
output:
v1 capacity: 10 v1 size: 10
v2 capacity: 10 v2 size: 10
vv capacity: 2 vv size: 2
v1 capacity: 20 v1 size: 20
v2 capacity: 20 v2 size: 20
vv capacity: 2 vv size: 2

Why std::make_move_iterator works on vector<string> but not on vector<int>

I was expecting that std::make_move_iterator will always move contents, but it seems not.
It looks like it is moving elements in vector<string> but not in vector<int>.
See the below code snippet:
#include <iostream>
#include <iterator>
#include <string>
#include <vector>
void moveIntVector()
{
std::cout << __func__ << std::endl;
std::vector<int> v1;
for (unsigned i = 0; i < 10; ++i) {
v1.push_back(i);
}
std::vector<int> v2(
std::make_move_iterator(v1.begin() + 5),
std::make_move_iterator(v1.end()));
std::cout << "v1 is: ";
for (auto i : v1) {
std::cout << i << " ";
}
std::cout << std::endl;
std::cout << "v2 is: ";
for (auto i : v2) {
std::cout << i << " ";
}
std::cout << std::endl;
}
void moveStringVector()
{
std::cout << __func__ << std::endl;
std::vector<std::string> v1;
for (unsigned i = 0; i < 10; ++i) {
v1.push_back(std::to_string(i));
}
std::vector<std::string> v2(
std::make_move_iterator(v1.begin() + 5),
std::make_move_iterator(v1.end()));
std::cout << "v1 is: ";
for (auto i : v1) {
std::cout << i << " ";
}
std::cout << std::endl;
std::cout << "v2 is: ";
for (auto i : v2) {
std::cout << i << " ";
}
std::cout << std::endl;
}
int main()
{
moveIntVector();
moveStringVector();
return 0;
}
The result is:
moveIntVector
v1 is: 0 1 2 3 4 5 6 7 8 9 # I expect this should be `0 1 2 3 4` as well!
v2 is: 5 6 7 8 9
moveStringVector
v1 is: 0 1 2 3 4
v2 is: 5 6 7 8 9
I'm on Ubuntu 14.04, gcc 4.8.2 and the code is compiled with -std=c++11
Could you explain why std::make_move_iterator have different behaviour on vector<int> and vector<string>? (Or is it a bug?)
The behaviour is expected. A move from both vectors leaves the original v1 with 5 moved-from elements in their second half.
The difference is that when the strings are moved, what is left behind is empty strings. This is because it is a very efficient way to move strings, and leave the moved-from string in a self-consistent state (Technically, they could be left to hold the value "Hello, World, nice move!", but that would incur extra cost). The bottom line is that you don't see those moved-from strings in your output.
In the case of the int vectors, there is no way to move an int that is more efficient than copying it, so they are just copied over.
If you check the sizes of the vectors, you will see the v1 have size 10 in both cases.
Here's a simplified example to illustrate that the moved from strings are left empty:
#include <iostream>
#include <iterator>
#include <string>
#include <vector>
int main()
{
std::vector<std::string> v1{"a", "b", "c", "d", "e"};
std::vector<std::string> v2(std::make_move_iterator(v1.begin()),
std::make_move_iterator(v1.end()));
std::cout << "v1 size " << v1.size() << '\n';
std::cout << "v1: ";
for (const auto& s : v1) std::cout << s << " - ";
std::cout << '\n';
std::cout << "v2 size " << v2.size() << '\n';
std::cout << "v2: ";
for (const auto& s : v2) std::cout << s << " - ";
std::cout << '\n';
}
Output:
v1 size 5
v1: - - - - -
v2 size 5
v2: a - b - c - d - e -
When we talk about a move we are not talking about moving the object itself (it remains intact). What gets moved are its internal data. This may or may not affect the value of the object whose internal data gets moved.
That is why your int array doesn't loose its original ints. As to your string example, it still has the original std::strings just like the int example but their internal values have changed to empty strings.
It is important to remember that internally a std::string (essentially) holds a pointer to a character array. So when you copy a std::string you copy every element of the character array. A move, however, avoids doing all that copying by copying the internal pointer instead.
But if the move operation stopped there that would leave both std::strings pointing at the same character array and changing the character data pointed to by either std::string would also change the other's. So when you move a string it is not enough to merely copy the internal pointer, you have to make the internal pointer of the std::string you moved from point to a new blank character array so that it can no longer affect the string its data was moved to.
When moving an int there is no further action required after the copy of its data. There are no pointers involved so after the copy both ints contain independent data.
move constructor is like of an object works like taking a regular reference and a instruction to move things. the default move constructor tries to call the move constructor of all member variables. a user defined one... pretty much it's up to the programmer to tell it what to do.
you could program your objects to be in a undefined state after being subject to a move constructor, you can keep them unchanged(the destructor will still be called so you need to take care of that), you can keep them valid. strings will have a defined state after being subject to a move constructor.
as for your example...
int is trivially copyable and it's move constructor won't do anything but copying.
string is not trivially copyable. it has some dynamic stuff in it that the move constructor moves. and the previous one is left with a length of zero, you ARE printing them, along with the trailing "space" which you added. it's just they are the last 5 elements, at the end of what your printing and you aren't noticing it because it's equivalent to 5 trailing white spaces.

push_back versus operator[] assignment in c++ vectors

can someone please explain to me in detail why the following code for vectorY will do the assignment but the size of VecY is zero? Also, the begin and end iterators are stuck at the first node. It seems that reserve only works with push back and that you need to construct the vector with the size if you want the iterators for the vectors and the size to work as expected. I am assuming that push_back is doing some type of allocation that the straight assignment is not in this case? I am looking for details explaining this so I can make sure I understand what is happening with a reserve and push_back versus constructing with a size element and then doing assignment as in VecX example.
#include <iostream>
#include <vector>
int main ( int argc, char *argv[])
{
std::vector<int> vecX(2);
vecX[0] = 1;
vecX[1] = 2;
std::cout << " VecX0 Item: " << vecX[0] << std::endl;
std::cout << " VecX1 Item: " << vecX[1] << std::endl;
std::cout << " VectorX Size: " << vecX.size() << std::endl;
std::vector<int> vecY;
vecY.reserve(2);
vecY[0] = 1;
vecY[1] = 2;
std::cout << " VecY0 Item: " << vecY[0] << std::endl;
std::cout << " VecY1 Item: " << vecY[1] << std::endl;
std::cout << " VectorY Size: " << vecY.size() << std::endl;
}
Output
VecX0 Item: 1
VecX1 Item: 2
VectorX Size: 2
VecY0 Item: 1
VecY1 Item: 2
VectorY Size: 0
std::vector<int> vecY;
vecY.reserve(2);
vecY[0] = 1;
vecY[1] = 2;
This code is wrong and evokes Undefined Behavior1. When you reserve a vector, you set the capacity, not the size.
You need to either push_back, or construct the vector as you did in example 1.
"Undefined Behavior" : This invokes Undefined Behavior because of the out-of-range call to operator[] If you call vector::operator[n] where n > vec.size(), the behavior is Undefined.
If you don't want use push_back nor construct, consider using the resize method