英文:
How should I initialise a shared-pointer member?
问题
-
传递引用:
class MyClass { std::shared_ptr<std::string> s1; public: MyClass(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 World");
MyClass myClass {s2};
MyClass2 myClass2 {s2};
std::cout << "Use count:" << s2.use_count() << std::endl;
return 0;
}
结果:
Use count:3
我认为第二种方式没有比第一种方式更有优势。
我认为第二种方式不应该使用,因为多了一个移动操作。
在性能方面,你认为哪种方式更好来初始化std::shared_ptr
?
更新
我可以根据下面的代码更新MyClass
,以便可以像这样调用它:
MyClass myClass {std::make_shared<std::string>("Hello World")};
性能在太多循环中可能会有所不同。
基准测试
#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 World");
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;
}
在线C++编译器和C++17:
0.358915
0.623789
英文:
I can initialize a std::shared_ptr
member in 2 different ways in my constructor (which both increment its use_count
):
-
Pass by reference:
class MyClass { std::shared_ptr<std::string> s1; public: MyClass(std::shared_ptr<std::string>& s2): s1{s2} {} };
-
Pass by value
class MyClass2 { std::shared_ptr<std::string> s1; public: MyClass2(std::shared_ptr<std::string> s2): s1{std::move(s2)} {} };
Test program:
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;
}
Result:
Use count:3
I don't see any advantage of the 2nd way over the 1st way.
I think the 2nd way should never be used because there is an extra move operation.
Which way do you think is better to initialise a std::shared_ptr
, in terms of performance?
Updated
I can update MyClass
according to below so it can be called like:
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} {}
};
Performance I guess makes a difference in too many loops.
Benchmark
#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;
}
Online c++ compiler and c++17
0.358915
0.623789
答案1
得分: 9
第一种方式需要至少一个shared_ptr
的副本(从s2
到s1
),无论调用函数做什么,都需要获取锁、增加和减少引用计数以及其他开销。此外,由于第一种版本需要一个shared_ptr&
,所以如果调用函数有一个临时的shared_ptr&&
,那么它们被迫将其存储在变量中,然后构造该类:
std::shared_ptr<std::string> s2 = std::make_shared<std::string>("Hello World");
MyClass myClass {s2};
第二种方式允许调用者将一个shared_ptr
值移入构造函数,然后直接移动到s1
。移动操作更加便宜:它们不需要锁定,并且不会增加或减少引用计数。它们非常快速。此外,由于第二种方式只接受一个值,因此可以以任何方式构造,因此可以直接从临时值构造:
MyClass myClass {std::make_shared<std::string>("Hello World")};
因此,第二种方式在各个方面都更优越。
在我的测试中,与您的测试相似,move
版本快约10%。
https://quick-bench.com/q/P3pX-bC8kRXSEmqJw2_KSz-kc0s
英文:
The first way requires at least one copy of the shared_ptr
(from s2
to s1
), regardless of what the calling function does, which requires grabbing locks, incrementing and decrementing the reference count, and other overhead.
Also, since the first version requires a shared_ptr&
, then if the calling function has a temporary shared_ptr&&
, then they're forced to store it in a variable before constructing the class:
std::shared_ptr<std::string> s2 = std::make_shared<std::string>("Hello World");
MyClass myClass {s2};
The second way allows the caller to move a shared_ptr
value into the constructor, which is then moved directly into s1
. Move operations are cheaper: they do not require locks, and they do not increment or decrement the reference count. They're insanely fast. Also, since the second one just takes a value, it can be constructed in any way, and therefore can be constructed directly from temporaries:
MyClass myClass {std::make_shared<std::string>("Hello World")};
So the second way is superior in every way.
In my tests that mirror your tests, the move
version is ~10% faster.
https://quick-bench.com/q/P3pX-bC8kRXSEmqJw2_KSz-kc0s
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论