std :: unique_ptr和自定义删除器

时间:2017-03-10 10:00:54

标签: c++ unique-ptr

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情况呢?

1 个答案:

答案 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指针。