cpp make_shared用于void指针

ted*_*ted 1 c++ smart-pointers c++11

我想使用std :: make_shared来创建一个void指针.因为make_shared应该比shared_ptr(new T)快,并且异常保存我想知道是否有一个库函数以make_shared方式创建shared_ptr(new foo).

How*_*ant 14

您可以将任何转换shared_ptr<foo>shared_ptr<void>与之相关的效率损失make_shared:

#include <memory>

struct foo {};

int main()
{
    std::shared_ptr<void> p = std::make_shared<foo>();
}
Run Code Online (Sandbox Code Playgroud)

转换使得foo和引用计数保持在相同的内存分配中,即使您现在通过a引用它void*.

更新

这是如何运作的?

一般结构std::shared_ptr<foo>是两个指针:

                          +------> foo
                          |         ^
p1  ---------> (refcount, +)        |
p2  --- foo* -----------------------+
Run Code Online (Sandbox Code Playgroud)

p1指向包含引用计数的控制块(实际上是两个引用计数:一个用于强所有者,一个用于弱所有者),一个删除器,一个分配器和一个指向该对象的"动态"类型的指针."动态"类型是shared_ptr<T>构造函数所看到的对象的类型(比如Y可能与a相同或不同T).

p2具有类型T*,其中T相同T如在shared_ptr<T>.可以将其视为存储对象的"静态"类型.当你取消引用a时shared_ptr<T>,它会p2被取消引用.当你破坏a时shared_ptr<T>,如果引用计数变为零,则控制块中的指针有助于破坏foo.

在上图中,控制块和foo动态分配. p1是一个拥有指针,控制块中的指针是一个拥有指针. p2是一个非拥有指针. p2唯一功能是解引用(箭头操作符,get()等).

使用时make_shared<foo>(),实现有机会将foo权限放在控制块中,与引用计数和其他数据一起:

p1  ---------> (refcount, foo)
p2  --- foo* --------------^
Run Code Online (Sandbox Code Playgroud)

这里的优化是现在只有一个分配:现在嵌入的控制块foo.

当上述内容转换为a时shared_ptr<void>,所发生的一切都是:

p1  ---------> (refcount, foo)
p2  --- void* -------------^
Run Code Online (Sandbox Code Playgroud)

即类型p2的变化foo*void*.而已.(除了递增/递减引用计数以考虑临时的复制和销毁 - 可以通过构造来从rvalue中省略).当引用计数变为零时,它仍然是控制块,它会破坏foo发现的通道p1. p2不参与销毁行动.

p1实际上指向控制块的通用基类.此基类不知道foo存储在派生控制块中的类型.派生的控制块shared_ptr在实际对象类型Y已知时在构造函数中构造.但从那以后,shared_ptr只能通过一个与控制块通信control_block_base*.因此,通过虚函数调用发生破坏等事情.

C++ 11中shared_ptr<void>rvalue 的"移动构造" shared_ptr<foo>只需要复制两个内部指针,而不必操纵引用计数.这是因为shared_ptr<foo>无论如何rvalue 即将消失:

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

这可以在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;
}
Run Code Online (Sandbox Code Playgroud)

在转换构造之前,引用计数仅为1.在转换构造之后,引用计数仍为1,源在其析构函数运行之前指向任何内容.简而言之,这就是移动语义的乐趣!:-)