我想使用std :: make_shared来创建一个void指针。因为make_shared应该比shared_ptr(new T)快,并且异常保存我想知道是否有一个库函数以make_shared方式创建shared_ptr(new foo)。
答案 0 :(得分:14)
您可以将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
与T
中的shared_ptr<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*
。而已。 (除了递增/递减引用计数以考虑临时的复制和销毁 - 可以通过构造来从rvalue中省略)。当引用计数变为零时,它仍然是破坏通过foo
找到的p1
的控制块。 p2
不参与销毁行动。
p1
实际上指向控制块的通用基类。此基类不知道存储在派生控制块中的类型foo
。在实际对象类型shared_ptr
已知时,派生控制块在Y
的构造函数中构造。但从那时起,shared_ptr
只能通过control_block_base*
与控制块进行通信。因此,破坏就是通过虚函数调用发生的。
C ++ 11中来自右值shared_ptr<void>
的{{1}}的“移动构造”只需要复制两个内部指针,而不必操纵引用计数。这是因为无论如何,右值shared_ptr<foo>
即将消失:
shared_ptr<foo>
最明显的是// shared_ptr<foo> constructed and destructed within this statement
std::shared_ptr<void> p = std::make_shared<foo>();
构造函数源代码:
shared_ptr
在转换构造之前,引用计数仅为1.在转换构造之后,引用计数仍为1,源在其析构函数运行之前指向任何内容。简而言之,这就是移动语义的乐趣! : - )