std :: make_shared在VS2012中进行两次构造函数调用

时间:2014-08-31 20:22:24

标签: c++ visual-studio-2012 c++11 constructor

我写了一段简单的代码来试用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的作用有一个缺陷。任何人都可以向我解释为什么会这样吗?

谢谢。

2 个答案:

答案 0 :(得分:7)

致电时:

std::shared_ptr<MyClass> x = std::make_shared<MyClass>(MyClass());

这是发生的事情:

  1. 创建了内部MyClass()实例(第一个构造函数调用 - 默认调用)。
  2. 内部MyClass()是暂时的。
  3. 现在,make_shared完美地将您的临时MyClass转发给MyClass移动构造函数,因为MyClass声明了一个,临时工可以由右值引用MyClass&&约束。 (第二个构造函数调用 - 移动构造函数)。
  4. 现在,当您删除move-constructor时,会发生以下情况:

    1. 创建了内部MyClass()实例(第一个构造函数调用 - 默认调用)。
    2. 内部MyClass()是暂时的。
    3. 现在,make_shared完美转发您的临时MyClass,但由于MyClass没有移动构造函数,因此临时值受const MyClass&引用的约束{复制构造函数,因此调用了复制构造函数(第二个构造函数调用 - 复制构造函数)。
    4. 也就是说,传递给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()));

这是不必要的。