为何调用make_shared会触发两次构造函数调用?如何避免该问题?
问题解答
1. 你的猜测是否正确
你的判断部分符合实际:
- 拷贝构造函数调用确实来自参数传递逻辑:你传入的
a是左值对象,make_shared内部需要用传入的参数构造新的Node实例,左值默认匹配拷贝构造函数,因此触发了拷贝。 - 多余的移动构造是低优化等级+旧标准库实现导致的冗余操作:C11之后
make_shared使用完美转发传递参数,正常逻辑下不会产生多余的移动构造,你现在看到的移动调用是旧版libstdc在O0优化等级下的临时对象冗余操作,开启优化或者使用高版本C++标准编译后这部分调用会被消除。
2. 避免两次构造的方案
可以根据你的使用场景选择两种方案:
- 如果你不需要保留原对象
a的内容,调用make_shared时传入右值即可:把make_shared<Node>(a)改成make_shared<Node>(std::move(a)),这样只会触发一次移动构造,不会触发拷贝构造。 - 如果你不需要提前创建独立的
a对象,直接把构造参数传递给make_shared即可,完全不会产生多余的拷贝/移动操作。
3. 仅调用一次构造得到shared_ptr<Node>的实现
不需要提前创建栈上的Node对象,直接调用make_shared<Node>()即可,make_shared会在自己分配的内存块中直接构造Node实例,全程只会触发一次构造函数:
#include <iostream> #include <memory> using namespace std; class Node { public: Node() { cout << "Called contructor\n"; } ~Node() { cout << "Called destructor\n"; } Node(const Node& other) { cout << "Called copy-contructor\n"; } Node(Node&& other) { cout << "Called move-constructor\n"; } }; int main(){ // 不需要提前创建Node对象,直接给make_shared传构造参数 shared_ptr<Node> sp = make_shared<Node>(); return 0; }
上述代码的输出为:
Called contructor Called destructor
全程只有一次构造、一次析构,没有任何多余的拷贝、移动操作。
补充说明:你当前使用的libstdc++版本较老,默认编译规则对新特性支持不好,建议编译时添加参数
g++ test.cpp -std=c++17 -O2,可以触发标准要求的复制消除优化,去掉所有冗余的构造函数调用。
内容的提问来源于stack exchange,提问作者Yingfa
相关产品推荐
相关产品推荐

