2011-12-27 69 views
1

我想用std :: make_shared創建一個void指針。由於make_shared應該比shared_ptr(new T)快,並且異常保存,我不知道是否有一個庫函數以make_shared方式創建shared_ptr(new foo)。cpp make_shared for void指針

+1

它會創建什麼類型的對象? – 2011-12-27 14:41:53

+0

問題在於,必須有一些不同的語法才能解決這個問題。 make_void_shared (構造函數參數)。我沒有從static_pointer_cast (make_shared (bar1,...,barn)) – ted 2011-12-27 14:45:38

+1

的性能增益您要求一個shared_ptr來包裝一個void *,然後指向一個真實的對象?但是,這意味着什麼呢? – 2011-12-27 14:47:00

回答

14

您可以將任何shared_ptr<foo>shared_ptr<void>沒有與make_shared相關的效率損失:

#include <memory> 

struct foo {}; 

int main() 
{ 
    std::shared_ptr<void> p = std::make_shared<foo>(); 
} 

轉換保持在相同的內存分配foo與基準計,即使您現在可以通過引用它一個void*

更新

這是如何工作的?

一個std::shared_ptr<foo>的一般結構是兩個指針:

      +------> foo 
          |  ^
p1 ---------> (refcount, +)  | 
p2 --- foo* -----------------------+ 

p1指向控制塊含有一個引用計數(實際上是兩個引用計數:一個用於強所有者和一個用於弱所有者),一個刪除器,一個分配器和一個指向對象「動態」類型的指針。 「動態」類型是0​​構造函數看到的對象的類型,如Y(可能與T相同或不同)。

p2具有類型T*其中T相同Tshared_ptr<T>。把它看作存儲對象的「靜態」類型。當您取消引用shared_ptr<T>時,它將被取消引用p2。在破壞shared_ptr<T>時,如果引用計數爲零,則它是控制塊中的指針,它有助於銷燬foo

在上圖中,控制塊和foo都是動態分配的。 p1是擁有指針,並且控制塊中的指針是擁有指針。 p2是一個非擁有指針。 p2只有函數是取消引用(箭頭運算符,get()等)。

當您使用make_shared<foo>(),實施有把foo就在控制塊,引用計數和其他數據的旁邊的機會:

p1 ---------> (refcount, foo) 
p2 --- foo* --------------^ 

這裏的優化是,只有現在有單一分配:現在嵌入foo的控制塊。

當上述被轉換爲shared_ptr<void>,發生的是:

p1 ---------> (refcount, foo) 
p2 --- void* -------------^ 

p2的類型從foo*變爲void*。而已。 (除了增加/減少引用計數以考慮副本和臨時性的破壞 - 可以通過從右值構造來消除)。當引用計數爲零時,它仍然是控制塊,銷燬foo,通過p1找到。 p2不參與銷燬操作。

p1實際上指向控制塊的通用基類。該基類不知道存儲在派生控制塊中的foo類型。在實際對象類型Y已知的時候,導出的控制塊在shared_ptr的構造函數中構造。但從那時起,shared_ptr只能通過control_block_base*與控制塊進行通信。因此,像通過虛擬函數調用發生破壞的事情。

在C++ 11中,來自右值shared_ptr<foo>shared_ptr<void>的「移動構造」只需複製兩個內部指針,而不必操作引用計數。這是因爲右值shared_ptr<foo>是大概要走開:

// shared_ptr<foo> constructed and destructed within this statement 
std::shared_ptr<void> p = std::make_shared<foo>(); 

這可以最清楚地體現在shared_ptr構造源代碼:

template<class _Tp> 
template<class _Yp> 
inline _LIBCPP_INLINE_VISIBILITY 
shared_ptr<_Tp>::shared_ptr(shared_ptr<_Yp>&& __r, 
          typename enable_if<is_convertible<_Yp*, _Tp*>::value, __nat>::type) 
     _NOEXCEPT 
    : __ptr_(__r.__ptr_), 
     __cntrl_(__r.__cntrl_) 
{ 
    __r.__ptr_ = 0; 
    __r.__cntrl_ = 0; 
} 

轉換施工前的引用計數只有1 。在轉換結構之後,引用計數仍然是1,並且源在它的析構函數運行之前沒有指向任何東西。簡而言之,這就是移動語義的喜悅! :-)

+0

這是如何工作的?用編譯器優化(討厭依靠那些)?我的理解告訴我,用純粹的C++最好的我可以期望的是移動語義。在閱讀這篇文章後,我會期待一些更復雜的事情。 – ted 2011-12-27 19:46:57

+0

非常詳細的優秀答案!一些開銷(我害怕)似乎留下了(refcount增加,新的共享指針,其中p2的類型爲void,釋放共享指針(指向controlblock的指針,指向對象的指針),其中p2是實際類型)似乎儘管如此。糾正我,如果我錯了。 – ted 2011-12-27 21:51:33

+1

@ted:如果沒有轉換到'shared_ptr ',你將會用RVO(返回值優化)創建'p',這意味着沒有拷貝,沒有引用計數操作。在C++ 11中,轉換爲'shared_ptr '幾乎沒有成本,但不完全。 'shared_ptr '仍然是通過RVO創建的,但是是一個右值。然後優化這個右值的'shared_ptr '的構造,以簡單地複製兩個內部指針並將右值源的兩個內部指針歸零。這不會觸及引用計數。 2個負載和4個商店(全部非原子)是額外成本。 – 2011-12-27 22:39:43