我写了一段简单的代码来试用C ++ 11的make_shared。当我打电话时,我不明白为什么:
std::shared_ptr<MyClass> x = std::make_shared<MyClass>(MyClass());
调用默认构造函数并调用移动构造函数。这看起来很好,因为移动构造函数不会创建副本。但是如果我注释掉MyClass的move构造函数的实现,它会调用默认构造函数,然后是复制构造函数,这似乎会破坏make_shared的目的。
#include <iostream>
#include <memory>
//-----------------------------------------------------------
class MyClass {
public:
// default constructor
MyClass() :
_data(0.0)
{
_data = (float)3.14;
std::cout << "MyClass::default constructor - data=" << _data << " ; class=" << this << std::endl;
};
// copy constructor
MyClass(const MyClass& input)
{
_data = input._data;
std::cout << "MyClass::copy constructor - data=" << _data << " ; class=" << this << std::endl;
};
// move constructor
MyClass(MyClass&& other)
{
std::cout << "MyClass::move constructor(before) - data=" << _data << " ; class=" << this << std::endl;
_swap(*this, other);
std::cout << "MyClass::move constructor(after) - data=" << _data << " ; class=" << this << std::endl;
};
// destructor
~MyClass()
{
std::cout << "MyClass::destructor - data=" << _data << " ; class=" << this << std::endl;
};
private:
// swap
void MyClass::_swap(MyClass& X, MyClass& Y)
{
std::swap(X._data, Y._data);
}
// members
float _data;
};
//-----------------------------------------------------------
int main()
{
std::shared_ptr<MyClass> x = std::make_shared<MyClass>(MyClass());
std::cout << std::endl << "Address for x: " << x << std::endl;
std::cout << std::endl << "Press Enter to exit." << std::endl;
std::cin.ignore();
return 0;
}
上述代码的输出是:
MyClass::default constructor - data=3.14 ; class=000000000019F860
MyClass::move constructor(before) - data=0 ; class=00000000003C3440
MyClass::move constructor(after) - data=3.14 ; class=00000000003C3440
MyClass::destructor - data=0 ; class=000000000019F860
Address for x: 00000000003C3440
Press Enter to exit.
MyClass::destructor - data=3.14 ; class=00000000003C3440
如果我注释掉移动构造函数,则输出为:
MyClass::default constructor - data=3.14 ; class=000000000016FA00
MyClass::copy constructor - data=3.14 ; class=00000000001B3440
MyClass::destructor - data=3.14 ; class=000000000016FA00
Address for x: 00000000001B3440
Press Enter to exit.
MyClass::destructor - data=3.14 ; class=00000000001B3440
也许我对make_shared的作用有一个缺陷。任何人都可以向我解释为什么会这样吗?
谢谢。
答案 0 :(得分:7)
致电时:
std::shared_ptr<MyClass> x = std::make_shared<MyClass>(MyClass());
这是发生的事情:
MyClass()
实例(第一个构造函数调用 - 默认调用)。MyClass()
是暂时的。make_shared
完美地将您的临时MyClass
转发给MyClass
的移动构造函数,因为MyClass
声明了一个,临时工可以由右值引用MyClass&&
约束。 (第二个构造函数调用 - 移动构造函数)。现在,当您删除move-constructor时,会发生以下情况:
MyClass()
实例(第一个构造函数调用 - 默认调用)。MyClass()
是暂时的。make_shared
完美转发您的临时MyClass
,但由于MyClass
没有移动构造函数,因此临时值受const MyClass&
引用的约束{复制构造函数,因此调用了复制构造函数(第二个构造函数调用 - 复制构造函数)。也就是说,传递给std::make_shared
的参数实际上是构造函数的参数而不是实例本身。因此你应该写:
std::shared_ptr<MyClass> x = std::make_shared<MyClass>();
例如,如果您拥有以下签名的MyClass
构造函数:
MyClass(int x, float f, std::unique_ptr<int> p);
然后你会说:
std::shared_ptr<MyClass> x
= std::make_shared<MyClass>(123, 3.14f, std::make_unique<int>(5));
make_shared
保证这些参数将完美转发到MyClass
构造函数。
您可以将std::make_shared
辅助函数视为如下所示:
template <typename T, typename... Args>
auto make_shared(Args&&... args) -> std::shared_ptr<T>
{
return std::shared_ptr<T>(new T(std::forward<Args>(args)...));
}
注意:实际上make_shared
还为连续内存块中的引用计数器分配空间,绑定析构函数,并执行其他魔术。上面的代码片段只是一个例子来说明参数本身会发生什么。
答案 1 :(得分:3)
请使用:
std::shared_ptr<MyClass> x = std::make_shared<MyClass>();
这样,就不会创建临时。
std::make_shared<X>(args...)
将args ...传递给它正在创建的对象的X
构造函数。在您的情况下,您不希望传递给构造函数的参数。
如果传递MyClass()
,则创建一个默认构造的对象,然后将其作为参数传递给正在创建的对象。就好像你这样做了:
std::shared_ptr<MyClass> x(new MyClass(MyClass()));
这是不必要的。