Search code examples
c++vectorconstructorshared-ptrcopy-constructor

In C++, how to correctly obtain a shared pointer to a vector , minimizing the number of copy constructor calling?


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.


Solution

  • There are two usages in your code that cause extra copying.

    1. 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).

    2. 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:

    1. 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.

    2. 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.