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({});
Related
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
I have the following test code that's runnable under clang.
#include <algorithm>
#include <vector>
#include <iostream>
int main() {
std::vector<int> vs{1, 2, 4, 5};
std::vector<std::reference_wrapper<int>> vs1;
for (int i : vs) {
std::cout << "loop: " << i << std::endl;
vs1.emplace_back(i);
}
for (auto p : vs1) {
std::cout << p << std::endl;
}
return 0;
}
You can plug that into https://rextester.com/l/cpp_online_compiler_clang (or locally). The result is:
loop: 1
loop: 2
loop: 4
loop: 5
5
5
5
5
I'd expect 1,2,4,5, not 5 all the way.
The code won't work on non-clang. Where's the problem?
i is a local variable inside its declaring for loop. It is a copy of each int in the vs vector. You are thus (via the emplace_back() call) creating reference_wrapper objects that refer to a local variable, keeping the references alive after the lifetime of the referred-to variable (i) has ended. This is undefined behavior.
The fix is to make i be a reference to each int, not a copy, that way the reference_wrappers refer to the ints in vs as expected:
for (int& i : vs)
First, you forgot <functional> header.
Second, reference_wrapper<int> stores reference to an int. Not its value. So in this loop:
for (int i : vs) {
std::cout << "loop: " << i << std::endl;
vs1.emplace_back(i);
}
You are changing value of i but not its place in memory. It is always the same variable. That's why it prints the last value stored in that variable, which is 5.
You may imagine this range- based for loop
for (int i : vs) {
std::cout << "loop: " << i << std::endl;
vs1.emplace_back(i);
}
the following way
for ( auto first = vs.begin(); first != vs.end(); ++first )
{
int i = *first;
vs1.emplace_back(i);
}
that is within the loop you are dealing with the local variable i that will not be alive after exiting the loop.
You need to use a reference to elements of the vector like
for (int &i : vs) {
std::cout << "loop: " << i << std::endl;
vs1.emplace_back(i);
}
Consider the following code:
#include <vector>
#include <iostream>
void monitor_vector(std::vector<int> myV)
{
std::vector<int>::iterator it = myV.begin();
std::cout << "Vector size: " << myV.size() << std::endl;
while (*it)
{
std::cout << "Element " << it-myV.begin() << ": " << *it << std::endl;
it++;
}
}
int main()
{
std::vector<int> myVector(4,1);
monitor_vector(myVector);
return 0;
}
Basically, I create a std::vector<int> that has 4 elements, and assign each element 1. When I execute this code, all is well. The output shows each element.
However, when I execute it with this line
std::vector<int> myVector(4,1);
replaced by this line:
std::vector<int> myVector(4,0);
I can't see any output. The output is:
yilmazali#yilmazali:~/cpp_practice$ g++ -o la vector_practice.cpp
yilmazali#yilmazali:~/cpp_practice$ ./la
Vector size: 4
yilmazali#yilmazali:~/cpp_practice$
Why does it ignore the elements with default integer value? Still, they are there as the member of std::vector.
Thanks for your time,
Ali
You iterate while (*it).
If you fill your vector with zeros, *it == 0 and therefore if (*it) is like if (false): the loop body is never executed.
Instead, loop while it != myV.end()
Is there any alternative to a range-based for loop when it comes to vector arrays? I've noticed that c++98 won't allow range-based for loops. Here is some sample code looping through a vector array using a range based for loop:
#include <iostream>
#include <vector>
using namespace std;
int main() {
vector<int> vecArray1 (3,20);
for (int v: vecArray1) {
cout << "ArrayValue:" << v << endl;
}
return 0;
}
Now here is an alternative I've tried that didn't work:
#include <iostream>
#include <vector>
using namespace std;
int main() {
vector<int> vecArray1 (3,20);
for (int i=0; i<sizeof(vecArray1); ++i) {
cout << "ArrayValue:" << vecArray1[i] << endl;
}
return 0;
}
It outputs 10 elements instead of 3 defined by the vector array above. Are there any alternatives to range-based for loops that work with c++98?
C++98 does not allow for range-based for-loops.
In C++98 you would need to do the following:
for(unsigned int i=0;i<vecArray.size();++i)
std::cout << "ArrayValue: " << vecArray[i] << std::endl;
or
for(std::vector<int>::iterator it=vecArray.begin();it!=vecArray.end();++it)
std::cout << "ArrayValue: " << *it << std::endl;
The operator
sizeof
does NOT give you the length of an array. Instead, it returns an unsigned integer representing the number of bytes of the type you give as the argument.
For example,
std::cout << sizeof(unsigned long long) << std::endl;
prints
8
on my machine, because the type unsigned long long consista of 64-bits, or 64/8 = 8 bytes.
In C++11 we now have range-based for-loops:
Examples:
for(int i : vecArray)
std::cout << "i = " << i << std::endl;
for(const int& i : vecArray)
std::cout << "i = " << i << std::endl;
In the first example the values are copied from vecArray into the variable i. In the second example you are instead working with const references to the values in vecArray (which could be useful for objects which are expensive to copy).
In fact, the range-based for loops in C++11 are available for all types on which you can call begin() and end() (i.e. those which you can iterate through).
sizeof(vecArray1) doesn't do what you think it does.
Either use:
for (int i=0; i<vecArray1.size(); ++i) {
cout << "ArrayValue:" << vecArray1[i] << endl;
}
or:
for (std::vector<int>::iterator it = vecArray1.begin(); it != vecArray1.end(); it++) {
cout << "ArrayValue: " << *it << endl;
}
The second one is more verbose, but works for other types of collections as well (like std::list) and is more similar to what the range-based for loop does.
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).