c-cpp make_shared用于无效指针
作者:互联网
我想使用std :: make_shared创建一个空指针.由于make_shared应该比shared_ptr(new T)快,并且可以保存异常,所以我想知道是否有一个库函数以make_shared的方式创建shared_ptr(new foo).
解决方法:
您可以转换任何shared_ptr< foo>到shared_ptr< void>在不损失与make_shared相关联的效率的情况下:
#include <memory>
struct foo {};
int main()
{
std::shared_ptr<void> p = std::make_shared<foo>();
}
转换使foo和引用计数保持在相同的内存分配中,即使您现在通过void *引用它也是如此.
更新
这是如何运作的?
std :: shared_ptr< foo>的一般结构有两个指针:
+------> foo
| ^
p1 ---------> (refcount, +) |
p2 --- foo* -----------------------+
p1指向包含参考计数(实际上是两个参考计数:一个用于强所有者,一个用于弱所有者),删除器,分配器和指向对象“动态”类型的指针的控制块. “动态”类型是shared_ptr< T>所包含的对象的类型.构造函数说,Y(可能与T相同或不同).
p2具有类型T *,其中T与shared_ptr< T>中的T相同.可以将其视为存储对象的“静态”类型.当您取消引用shared_ptr< T>时,p2将被取消引用.当您破坏shared_ptr< T>时,如果引用计数为零,则是控制块中的指针有助于破坏foo.
在上图中,控制块和foo都是动态分配的. p1是拥有者指针,而控制块中的指针是拥有者指针. p2是一个非所有指针. p2的唯一功能是取消引用(箭头运算符,get()等).
当您使用make_shared< foo>()时,实现可以将foo与引用计数和其他数据一起放在控制块中:
p1 ---------> (refcount, foo)
p2 --- foo* --------------^
这里的优化是现在只有一个分配:现在嵌入foo的控制块.
当上述内容转换为shared_ptr< void>时,发生的一切是:
p1 ---------> (refcount, foo)
p2 --- void* -------------^
即p2的类型从foo *变为void *.而已. (除了增加/减少参考计数以外,还考虑了临时副本的复制和销毁-可以通过构造从右值中消除它).当引用计数变为零时,仍然是控制块会破坏通过p1找到的foo. p2不参与销毁操作.
p1实际上指向控制块的通用基类.该基类不知道存储在派生的控制块中的foo类型.在知道实际对象类型Y时,将在shared_ptr的构造函数中构造派生的控制块.但是从那时起,shared_ptr只能通过control_block_base *与控制块进行通信.因此,诸如破坏之类的事情是通过虚函数调用发生的.
shared_ptr< void>的“移动构造”是指将其作为对象.来自右值shared_ptr< foo> C 11中的C语言只需要复制两个内部指针,而不必操纵引用计数.这是因为右值shared_ptr< foo>无论如何都会消失:
// shared_ptr<foo> constructed and destructed within this statement
std::shared_ptr<void> p = std::make_shared<foo>();
在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;
}
在转换构造之前,参考计数仅为1.在转换构造之后,参考计数仍为1,而在其析构函数运行之前,源不指向任何内容.简而言之,这就是移动语义的乐趣!
标签:c,c11,smart-pointers 来源: https://codeday.me/bug/20191009/1879124.html