Reputation: 421
I need a function which returns a shared_ptr to a vector containing a large number of objects. The following code realizes this but one can see that copy constructors are called for an extra number of times.
#include <iostream>
#include <vector>
#include <memory>
using namespace std;
class A {
public:
int IA ;
A(int ia) {
cout << "Constructor is called\n" ;
IA = ia ;
}
A(const A& a) {
cout << "Copy constructor is called\n" ;
IA = a.IA ;
}
} ;
shared_ptr<vector<A>> foo() {
vector<A> myA ;
myA.push_back(A(10)) ;
myA.push_back(A(20)) ;
return make_shared<vector<A>>(myA) ;
}
int main() {
shared_ptr<vector<A>> myA ;
myA = foo() ;
return 0 ;
}
In real cases, A may also be bigger, so to make_shared<vector<A>>(myA) ;
would cause unncessary copying process. I wonder if there is any way to reduce copy constructor calling times.
Upvotes: 2
Views: 218
Reputation: 1062
There are two usages in your code that cause extra copying.
myA.push_back(A(10))
will create a temporary object of class A
, which is then copied into the vector myA
. Note that we cannot reduce such duplication by changing to myA.emplace_back(A(10))
. It will also creates a temporary A
object, and then calls the constructor of A
with the temporary (xvalue) as an argument. This will call the copy constructor (since you didn't provide move constructor).
make_shared<vector<A>>(myA)
will create the managed object in std::shared_ptr
from myA
, which will be copied. Copying a vector will copy all of its elements.
Solutions:
Provide appropriate arugment to emplace_back
. Here we can write myA.emplace_back(10)
. Now it will call the constructor of A
with argument 10
, which will select the constructor A(int)
. That is, construct the object of class A
directly in the vector, instead of creating a temporary and then copying it into the vector.
Move myA
into the shared_ptr
by make_shared<vector<A>>(std::move(myA))
. Here it will not copy the elements in the vector.
Note that as the capacity of the vector increases, its elements will also be copied. There are some solutions. If you know the number of elements, you can use reserve()
to pre-allocate enough memory. You can also previde a noexcept move constructor for class A
to avoid copying when changing the capacity.
Upvotes: 1
Reputation: 421
Thanks! The code now is :
shared_ptr<vector<A>> foo2() {
auto ptr = make_shared<vector<A>>() ;
(*ptr).reserve(99) ;
ptr->emplace_back(A(10)) ;
ptr->emplace_back(A(20)) ;
return ptr ;
}
int main() {
shared_ptr<vector<A>> myA, myA2 ;
myA2 = foo2() ;
cout << (*myA2)[0].IA << endl;
return 0 ;
}
The number of copy constructor calling is reduced to two. Is it possible to continue to avoid calling the copy constructor at all, for I see no reason to make a copy after construction.
Upvotes: 0
Reputation: 122133
This line
return make_shared<vector<A>>(myA) ;
Is making an unnecessary copy. You do not need to create a vector first then copy it to another dynamically allocated vector. You only need to create one vector:
shared_ptr<vector<A>> foo() {
return make_shared<vector<A>>(std::initializer_list<A>{{10},{20}});
}
Upvotes: 1