我应该如何初始化共享指针成员?

Met*_*isi 3 c++ smart-pointers shared-ptr c++11

我可以std::shared_ptr在构造函数中以两种不同的方式初始化成员(这两种方式都会增加它的值use_count):

  1. 通过参考传递:

    class MyClass {
        std::shared_ptr<std::string> s1;
    public:
        MyClass(std::shared_ptr<std::string>& s2): s1{s2} {}
    };
    
    Run Code Online (Sandbox Code Playgroud)
  2. 按值传递

    class MyClass2 {
        std::shared_ptr<std::string> s1;
    public:
        MyClass2(std::shared_ptr<std::string> s2): s1{std::move(s2)} {}
    };
    
    Run Code Online (Sandbox Code Playgroud)

测试程序:

int main() {
    std::shared_ptr<std::string> s2 = std::make_shared<std::string>("Hello World");
    MyClass myClass {s2};
    MyClass2 myClass2 {s2};
    
    std::cout << "Use count:" << s2.use_count() << std::endl;
        
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

结果:

class MyClass {
    std::shared_ptr<std::string> s1;
public:
    MyClass(std::shared_ptr<std::string>& s2): s1{s2} {}
};
Run Code Online (Sandbox Code Playgroud)

我不认为第二种方式比第一种方式有任何优势。我认为永远不应该使用第二种方式,因为有额外的移动操作。

std::shared_ptr就性能而言,您认为哪种方式初始化 a 更好?

更新

我可以MyClass根据下面的内容进行更新,因此可以这样调用: MyClass myClass {std::make_shared<std::string>("Hello World")};

class MyClass {
    std::shared_ptr<std::string> s1;
public:
    MyClass(const std::shared_ptr<std::string>& s2): s1{s2} {}
};
Run Code Online (Sandbox Code Playgroud)

我认为性能在太多循环中会产生影响。

基准

#include <iostream>
#include <memory>
#include <chrono>

using namespace std;

class MyClass {
    std::shared_ptr<std::string> s1;
public:
    MyClass(const std::shared_ptr<std::string>& s2): s1{s2} {}
};

class MyClass2 {
    std::shared_ptr<std::string> s1;
public:
    MyClass2(std::shared_ptr<std::string> s2): s1{std::move(s2)} {}
};


int main() {
    std::shared_ptr<std::string> s2 = std::make_shared<std::string>("Hello Wolrd");
    
    auto start = std::chrono::high_resolution_clock::now();
    
    for (int i = 0; i < 10000000; i++) {
        MyClass myClass {s2};
    }    
    auto end = std::chrono::high_resolution_clock::now();
    
    std::chrono::duration<float> duration = end - start;
    std::cout << duration.count() << std::endl;
    
    
    
    start = std::chrono::high_resolution_clock::now();
    
    for (int i = 0; i < 10000000; i++) {
        MyClass2 myClass2 {s2};
    }    
    end = std::chrono::high_resolution_clock::now();
    
    duration = end - start;
    std::cout << duration.count() << std::endl;    
        
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

在线c++编译器和c++17

0.358915
0.623789
Run Code Online (Sandbox Code Playgroud)

Moo*_*uck 9

第一种方法需要至少一个shared_ptr(from s2to s1) 的副本,无论调用函数做什么,这需要获取锁、递增和递减引用计数以及其他开销。另外,由于第一个版本需要 a shared_ptr&,那么如果调用函数有一个临时的shared_ptr&&,那么它们必须在构造类之前将其存储在变量中:

    std::shared_ptr<std::string> s2 = std::make_shared<std::string>("Hello World");
    MyClass myClass {s2};
Run Code Online (Sandbox Code Playgroud)

第二种方式允许调用者将一个shared_ptr值移动到构造函数中,然后将其直接移动到s1. 移动操作更便宜:它们不需要锁,并且不增加或减少引用计数。他们的速度快得离谱。此外,由于第二个只接受一个值,因此可以以任何方式构造它,因此可以直接从临时变量构造:

    MyClass myClass {std::make_shared<std::string>("Hello World")};
Run Code Online (Sandbox Code Playgroud)

所以第二种方式在各方面都更胜一筹。

在我的测试中,反映了您的测试,该move版本快了约 10%。 https://quick-bench.com/q/P3pX-bC8kRXSEmqJw2_KSz-kc0s