2014-08-31 96 views
2

我寫了一段簡單的代碼來嘗試make_shared for C++ 11。我不明白爲什麼當我打電話時:std :: make_shared在VS2012中做了兩個構造函數調用

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

默認的構造函數被調用,並調用一個移動構造函數。這看起來很好,因爲移動構造函數不會創建副本。但是如果我註釋掉MyClass的移動構造函數的實現,它會調用默認的構造函數,接着是複製構造函數,這似乎違背了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

它看起來很簡單 - 'make_shared'需要將您創建的未命名的MyClass()'temp顯式複製/移動到它所分配的共享對象空間中。如果可以的話,它使用移動構造函數,如果沒有移動構造函數,則使用複製構造函數。你期望發生什麼? – 2014-08-31 20:38:39

+0

我真的想(在我的腦海裏)做Piotr S.所建議的,所以它只做一個構造函數調用。我沒有完全理解make_shared的語法。謝謝您的意見。 – Mike 2014-08-31 23:59:47

回答

6

當你撥打:

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

這是發生了什麼:

  1. 創建內部MyClass()實例(第一構造函數調用 - 一個默認)。
  2. 內部MyClass()是一個臨時的。
  3. 現在,make_shared完美批轉臨時MyClass移動構造函數的MyClass,因爲MyClass聲明一個和右值引用MyClass&&的臨時可以綁定。 (第二個構造函數調用 - 移動構造函數)。現在

,當您刪除移動構造函數,這是發生了什麼:

  1. MyClass()實例被創建(在第一構造函數調用 - 一個默認)。
  2. 內部MyClass()是一個臨時的。
  3. 現在,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還分配了一個參考的櫃檯空間連續的存儲塊,結合析構函數,並做了其他魔術。上面的片段只是一個例子來說明參數本身會發生什麼。

+0

你的建議是我真正需要做的。感謝您的詳細回覆! – Mike 2014-08-31 23:57:30

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())); 

這是不必要的。

+0

謝謝你的建議。我現在明白這是我需要做的。 – Mike 2014-09-01 00:00:40

相關問題