在C++中,如何正确获取指向向量的共享指针,最大限度地减少复制构造函数调用的次数?

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

提问人:roy.atlas 提问时间:6/22/2022 更新时间:6/23/2022 访问量:188

问:

我需要一个函数,该函数将shared_ptr返回到包含大量对象的向量。下面的代码实现了这一点,但可以看到复制构造函数被调用了额外的次数。

#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 ;
}

在实际情况下,A 也可能更大,因此会导致不必要的复制过程。我想知道是否有任何方法可以减少复制构造函数调用时间。make_shared<vector<A>>(myA) ;

C++ Vector shared-ptr 复制构造函数

评论

2赞 Goswin von Brederlow 6/22/2022
为什么不先make_shared向量,然后保留空间并emplace_back?
0赞 Homer512 6/22/2022
是的,只需先调用make_shared并直接对该向量进行操作即可。如果您有一个向量,但后来才决定将其共享,请使用移动构造函数进行构造以避免昂贵的副本。std::make_shared<vector<A>>(std::move(myA));
0赞 Jarod42 6/22/2022
你真的需要分享吗?vector
0赞 Oliort UA 6/22/2022
如果要从仍没有任何共享指针的向量创建共享指针,可以通过 将原始向量的内容移动到新构造的向量。做std::make_sharedreturn make_shared<vector<A>>(std::move(myA));
2赞 molbdnilo 6/22/2022
make_shared从其参数中创建一个具有共享所有权的新对象。如果您不想要副本,请不要先制作原件。您不能获取现有对象并“使其共享”。

答:

1赞 463035818_is_not_an_ai 6/22/2022 #1

这条线

return make_shared<vector<A>>(myA) ; 

正在制作不必要的副本。您无需先创建向量,然后将其复制到另一个动态分配的向量。您只需要创建一个向量:

shared_ptr<vector<A>> foo() {
    return make_shared<vector<A>>(std::initializer_list<A>{{10},{20}}); 
}
0赞 roy.atlas 6/23/2022 #2

谢谢!现在的代码是:

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 ;
}

复制构造函数调用的次数减少到两个。是否可以继续避免调用复制构造函数,因为我认为没有理由在构造后制作副本。

评论

1赞 roy.atlas 6/24/2022
谢谢冥王星!看来我应该改用 emplace_back(10)。
1赞 Pluto 6/23/2022 #3

代码中有两种用法会导致额外的复制。

  1. myA.push_back(A(10))将创建一个 class 的临时对象,然后将其复制到 vector 中。请注意,我们不能通过更改为 来减少这种重复。它还将创建一个临时对象,然后使用临时 (xvalue) 作为参数调用构造函数。这将调用复制构造函数(因为您没有提供移动构造函数)。AmyAmyA.emplace_back(A(10))AA

  2. make_shared<vector<A>>(myA)将在 from 中创建托管对象,该对象将被复制。复制向量将复制其所有元素。std::shared_ptrmyA

解决 方案:

  1. 提供适当的 arugment to .在这里我们可以写.现在它将调用 with argument 的构造函数,它将选择构造函数。也就是说,直接在向量中构造类的对象,而不是创建一个临时的,然后将其复制到向量中。emplace_backmyA.emplace_back(10)A10A(int)A

  2. 进入 by .在这里,它不会复制向量中的元素。myAshared_ptrmake_shared<vector<A>>(std::move(myA))

请注意,随着向量容量的增加,其元素也将被复制。有一些解决方案。如果您知道元素的数量,则可以使用来预分配足够的内存。您还可以为类预置一个 noexcept 移动构造函数,以避免在更改容量时复制。reserve()A