STD::ALLOCATE_SHARED使用什么类型来分配内存?

发件人https://en.cppreference.com/w/cpp/memory/shared_ptr/allocate_shared:

template< class T, class Alloc, class... Args >
shared_ptr<T> allocate_shared( const Alloc& alloc, Args&&... args );
存储空间通常大于sizeof(T),以便为共享指针的控制块和T对象使用一个分配。..。所有内存分配都使用alloc的副本完成,该副本必须满足Allocator要求。

然后使用哪种类型来分配上述存储?换句话说,分配器要求之一Alloc::value_type应该是什么?


解决方案

实际使用的类型取决于实现。根据Allocator要求,借助std::allocator_traits特征类模板,可以通过std::allocator_traits<A>::rebind_alloc<T>机制将任何分配器rebind赋给其他类型。

假设您有一个分配器

template<class T>
class MyAlloc { ... };

如果您写:

std::allocate_shared<T>(MyAlloc<T>{});

这并不意味着将使用MyAlloc<T>执行分配。如果在内部需要分配另一种类型的对象S,我们可以通过

获得适当的分配器
std::allocator_traits<MyAlloc<T>>::rebind_alloc<S>

它的定义是,如果MyAlloc本身没有提供rebind<U>::other成员类型别名,则使用默认实现,该实现返回MyAlloc<S>。分配器对象是通过相应的MyAlloc<S>的转换构造函数(请参阅分配器要求)从传递给std::allocate_shared()(此处为MyAlloc<T>{})的对象构造的。

让我们来看看一些特殊的implementation-libstdc++。对于上面的行,实际分配由

执行
MyAlloc<std::_Sp_counted_ptr_inplace<T, Alloc<T>, (__gnu_cxx::_Lock_policy)2>

这是合理的:std::allocate_shared()为同时包含T和控制块的对象分配内存。_Sp_counted_ptr_inplace<T, ...>就是这样一个对象。它在_M_storage数据成员中包含T内部:

__gnu_cxx::__aligned_buffer<T> _M_storage;

许多其他地方也使用相同的机制。例如,std::list<T, Alloc>使用它来获取分配器,然后使用该分配器来分配列表节点,除了T之外,列表节点还保存指向其邻居的指针。

一个有趣的相关问题是,为什么分配器不是模板模板参数,这似乎是一个自然的选择。已讨论here。

相关文章