c++ - 为什么 shared_ptr 使用placement new

标签 c++ c++11

我在很多地方读到使用 make_shared<T> 时创建 shared_ptr<T> , 它的控制 block 包含一个足够大的存储 block 来容纳 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;
};

但我有点困惑,为什么我们不能只使用类型为 T 的成员变量反而?为什么要创建原始存储然后使用新放置?它不能与 T 类型的普通对象一步结合吗? ?

最佳答案

这是为了允许生命周期管理。

weak_count 为零之前,控制 block 不会被销毁。一旦 count 达到零,storage 对象就会被销毁。也就是说,当计数为零时,需要直接调用storage的析构函数,而在控制 block 的析构函数中。

为了防止控制 block 的析构函数调用storage的析构函数,storage的实际类型不能是T

如果我们只有强引用计数,那么 T 就可以了(而且更简单)。


实际上,实现比这要复杂一些。请记住,可以通过使用 new 分配 T,然后从中构造 shared_ptr 来构造 shared_ptr。因此 actual 控制 block 看起来更像:

template<typename T>
struct shared_ptr_control_block {
    std::atomic<long> count;
    std::atomic<long> weak_count;
    T* ptr;
};

make_shared分配的是:

template<typename T>
struct both {
    shared_ptr_control_block cb;
    std::aligned_storage_t<sizeof (T), alignof (T)> storage;
};

cb.p设置为storage的地址。在 make_shared 中分配 both 结构意味着我们得到一个内存分配,而不是两个(而且内存分配很昂贵)。

注意:我已经简化了:必须有一种方法让 shared_ptr 析构函数知道控制 block 是否是 both 的一部分(在这种情况下,在完成之前无法释放内存),或者不是(在这种情况下可以更早地释放它)。这可能是一个简单的 bool 标志(在这种情况下控制 block 更大),或者通过在指针中使用一些备用位(这不是可移植的 - 但标准库实现不必是可移植的)。为了避免在 make_shared 情况下存储指针根本,实现甚至可以更加复杂。

关于c++ - 为什么 shared_ptr 使用placement new,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41716083/

相关文章:

c++ - RAII 类的通用命名约定是什么?

c++ - 带模板的结构的动态分配

c++ - Clang 问题 : implicit type conversion at construction time

c++ - std::function 和 std::packaged_task 转换

c++ - 带有自定义比较器的优先级队列

c++ - Clang 无法使用 libstdc++ 识别 std::shared_ptr

C++ STL : Why allocators don't increase memory footprint of containers?

c++ - boost any library的典型用法是什么?

java - Android NDK/一般 JNI 问题 : Converting object/jobject to c++ user defiend type

c++ - 使用 gil 读取 png 图像