我在许多地方读过,当make_shared
用于创建一个时shared_ptr
,它的控制块包含一个足够大的存储块来容纳一个T
,然后该对象是在存储内部构建的,具有新的位置.像这样的东西:
templatestruct shared_ptr_control_block { std::atomic count; std::atomic weak_count; std::aligned_storage_t storage; };
但我有点困惑为什么我们不能只有一个成员变量与类型T
?为什么要创建原始存储然后使用新的贴图?不能与普通的类型对象一步合并T
吗?
它允许终身管理.
控制块直到weak_count
为零才被销毁.storage
一旦count
达到零,对象就会被销毁.这意味着你需要storage
在计数达到零时直接调用析构函数,而不是在控制块的析构函数中.
为了防止控制块的析构函数调用析构函数storage
,实际的类型storage
不能T
.
如果我们只有强引用计数,那么a T
会很好(并且更简单).
实际上,实现比这复杂一点.请记住,一个shared_ptr可以通过分配构成T
与new
,然后构建shared_ptr
从.因此实际控制块看起来更像:
templatestruct shared_ptr_control_block { std::atomic count; std::atomic weak_count; T* ptr; };
什么make_shared
分配是:
templatestruct both { shared_ptr_control_block cb; std::aligned_storage_t storage; };
并cb.p
设置为地址storage
.分配both
结构make_shared
意味着我们获得单个内存分配,而不是两个(并且内存分配很昂贵).
注意:我已经简化了:必须有一种方法让shared_ptr析构函数知道控制块是否属于both
(在这种情况下,内存在完成之前无法释放),或者不是(在这种情况下可以提前释放) .这可能是一个简单的bool标志(在这种情况下控制块更大),或者通过在指针中使用一些备用位(这是不可移植的 - 但标准库实现不必是可移植的).实现可能更复杂,以避免在该情况下完全存储指针make_shared
.
由于弱指针可能比存储的对象寿命长,因此控制块的寿命可能必须超过存储对象的寿命.如果托管对象是一个成员变量,它只能在控制块被销毁时被破坏(或者析构函数将被调用两次).
即使在对象本身被破坏之后存储仍然分配的事实实际上可能是make_shared
存储器约束系统的缺点(尽管我不知道这是否是在实践中遇到的事情).