2017-03-10 121 views
3

斯科特·梅耶的「有效的現代C++」,討論與定製刪除使用std::unique_ptr,並指出:的std ::的unique_ptr和自定義刪除器

刪除器是函數指針通常導致std::unique_ptr的大小從一個成長 字一二。對於作爲函數對象的刪除器,大小的改變取決於函數對象中存儲了多少狀態。無狀態函數對象(例如,來自沒有捕獲的lambda表達式)不會導致大小懲罰,這意味着當自定義刪除器可以作爲函數或無捕獲lambda表達式實現時,lambda是更可取的。

作爲一個例子,這樣的:

auto delInvmt1 = [](Investment* pInvestment) { 
makeLogEntry(pInvestment); 
delete pInvestment; 
}; 

template<typename... Ts> 
std::unique_ptr<Investment, decltype(delInvmt1)> 
makeInvestment(Ts&&... args); 

是比這更好的:

void delInvmt2(Investment* pInvestment) { 
    makeLogEntry(pInvestment); 
    delete pInvestment; 
} 

template<typename... Ts> 
std::unique_ptr<Investment, void (*)(Investment*)> 
makeInvestment(Ts&&... params); 

我可以看到,在第二種情況下的指針刪除器功能需要被存儲在unique_ptr,但爲什麼沒有類似的需要爲lambda情況存儲?

+2

'std :: unique_ptr'正在使用空基優化,它允許存儲空對象(即沒有數據成員的類),而沒有額外的大小開銷。 – milleniumbug

+4

在第一種情況下,邏輯是* type *的一部分,第二種情況是* value *的一部分。 –

回答

1

正如@milleniumbug所說,std::unique_ptr使用Empty Base Optimization。這意味着你可以聲明一個類沒有數據成員:

class empty 
{ 
public: 
    // methods 
}; 

如果您有申報empty裏面一個成員變量另一個類,類的規模甚至會增加,如果empty沒有數據成員:

class foo 
{ 
public: 
    int i; 
    empty em; 
}; 

在這種情況下,foo的大小將是8個字節。但是,如果你申報fooempty繼承,這種繼承具有在foo大小沒有影響,它的大小爲4個字節:

class foo : public empty 
{ 
public: 
    int i; 
}; 

如果你看一看到std::unique_ptr實現你的編譯器,你會看到這一點。我使用VC++ 2015和在std::unique_ptr該編譯器的結構是象下面這樣:

template<class _Ty, class _Dx> // = default_delete<_Ty> 
class unique_ptr : public _Unique_ptr_base<_Ty, _Dx> 

它從該類繼承:

template<class _Ty, class _Dx> 
class _Unique_ptr_base 
{ // stores pointer and deleter 
public: 
... 
_Compressed_pair<_Dx, pointer> _Mypair; 
}; 

_Unique_ptr_base_Compressed_pair類型的成員。這個類是這樣聲明的:

template<class _Ty1, class _Ty2, bool = is_empty<_Ty1>::value && !is_final<_Ty1>::value> 
class _Compressed_pair final 
    : private _Ty1 

{ // store a pair of values, deriving from empty first 
private: 
    _Ty2 _Myval2; 

其實這個類是專門化的,如果它的第二個模板參數是一個空類。在這種情況下,它從空的刪除器類繼承並聲明第一個模板參數的成員變量,即std::unique_ptr指針。

相關問題