【问题标题】:std::unique_ptr and custom deletersstd::unique_ptr 和自定义删除器
【发布时间】:2017-03-10 10:00:54
【问题描述】:

Scott Meyer 的“Effective Modern C++”讨论了 std::unique_ptr 与自定义删除器的使用并指出:

作为函数指针的删除器通常会导致 std::unique_ptr 的大小从 1 增长 两个字。对于作为函数对象的删除器,大小的变化取决于函数对象中存储了多少状态。无状态函数对象(例如,来自没有捕获的 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 情况不需要存储任何类似的东西呢?

【问题讨论】:

  • std::unique_ptr 正在使用空基优化,它允许存储空对象(即没有数据成员的类)而不会产生额外的大小开销。
  • 在第一种情况下,逻辑是 type 的一部分,在第二种情况下,它是 value 的一部分。

标签: c++ unique-ptr


【解决方案1】:

正如@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 指针。

【讨论】:

    猜你喜欢
    • 2013-03-30
    • 2015-04-09
    • 2017-05-15
    • 1970-01-01
    • 2018-01-02
    • 1970-01-01
    • 2018-01-31
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多