I am a bit confused with the way vector push_back behaves, with the following snippet I expected the copy constructor to be invoked only twice, but the output suggest otherwise. Is it a vector internal restructuring that results in this behaviour.
Output:
Inside default Inside copy with my_int = 0 Inside copy with my_int = 0 Inside copy with my_int = 1
class Myint
{
private:
int my_int;
public:
Myint() : my_int(0)
{
cout << "Inside default " << endl;
}
Myint(const Myint& x) : my_int(x.my_int)
{
cout << "Inside copy with my_int = " << x.my_int << endl;
}
void set(const int &x)
{
my_int = x;
}
}
vector<Myint> myints;
Myint x;
myints.push_back(x);
x.set(1);
myints.push_back(x);
A copy constructor of class T is a non-template constructor whose first parameter is T&, const T&, volatile T&, or const volatile T& , and either there are no other parameters, or the rest of the parameters all have default values. // Typical declaration of a copy constructor. // Forcing a copy constructor to be generated by the compiler.
vector::push_back () push_back () function is used to push elements into a vector from the back. The new value is inserted into the vector at the end, after the current last element and the container size is increased by 1.
vector::push_back() and vector::pop_back() in C++ STL. Vectors are same as dynamic arrays with the ability to resize itself automatically when an element is inserted or deleted, with their storage being handled automatically by the container. vector::push_back() push_back() function is used to push elements into a vector from the back.
Hence, “ Copied ” is printed thrice as the copy constructor is called thrice, once for each element inserted. However, if the size of the vector is fixed, then before inserting any element, the number of times “Copied” will be printed is one.
What happens:
x
is inserted via push_back
. One copy occurs: The newly created element is initialized with the argument. my_int
is taken over as zero because x
s default constructor initialized it so.
The second element is push_back
'd; The vector needs to reallocate the memory since the internal capacity was reached.
As no move constructor is implicitly defined for Myint
1 the copy constructor is chosen; The first element is copied into the newly allocated memory (its my_int
is still zero... so the copy constructor shows my_int
as 0
again) and then x
is copied to initialize the second element (as with the first in step 1.). This time x
has my_int
set to one and that's what the output of the copy constructor tells us.
So the total amount of calls is three. This might vary from one implementation to another as the initial capacity might be different. However, two calls are be the minimum.
You can reduce the amount of copies by, in advance, reserving more memory - i.e. higher the vectors capacity so the reallocation becomes unnecessary:
myints.reserve(2); // Now two elements can be inserted without reallocation.
Furthermore you can elide the copies when inserting as follows:
myints.emplace_back(0);
This "emplaces" a new element - emplace_back
is a variadic template and can therefore take an arbitrary amount of arguments which it then forwards - without copies or moves - to the elements constructor.
1 Because there is a user-declared copy constructor.
You got it...it was the resizing. But I'll just point out that if you're doing some bean counting on your constructors, you might be interested in "emplacement":
#include <iostream>
#include <vector>
using namespace std;
class Myint
{
private:
int my_int;
public:
explicit Myint(int value = 0) : my_int(value)
{
cout << "Inside default " << endl;
}
Myint(const Myint& x) : my_int(x.my_int)
{
cout << "Inside copy with my_int = " << x.my_int << endl;
}
Myint(const Myint&& x) noexcept : my_int(x.my_int) {
cout << "Inside move with my_int = " << x.my_int << endl;
}
};
int main() {
vector<Myint> myints;
myints.reserve(2);
myints.emplace_back(0);
myints.emplace_back(1);
// your code goes here
return 0;
}
That should give you:
Inside default
Inside default
And, due to the noexcept on the move constructor...if you delete the reserve
you'd get a move, not a copy:
Inside default
Inside default
Inside move with my_int = 0
There's no real advantage with this datatype of a move over a copy. But semantically it could be a big difference if your data type was more "heavy weight" and had a way of "moving" its members that was more like transferring ownership of some pointer to a large data structure.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With