Scott Meyer的“Effective Modern C ++”讨论了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情况呢?
答案 0 :(得分:2)
正如@milleniumbug所说,std::unique_ptr
使用Empty Base Optimization
。这意味着您可以声明一个没有数据成员的类:
class empty
{
public:
// methods
};
如果你有另一个在其中声明empty
成员变量的类,即使empty
没有数据成员,你的类的大小也会增加:
class foo
{
public:
int i;
empty em;
};
在这种情况下,foo
的大小为8字节。但是,如果您声明foo
继承自empty
,则此继承对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
指针。