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