You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++20:获取std::allocate_shared分配大小的标准合规方法及池分配器适配

高效结合boost::pool_allocator与std::allocate_shared的可行方案

1. 分配器适配层:按需匹配对应大小的内存池

由于boost::pool_allocator针对固定大小内存块优化,而std::allocate_shared会一次性分配控制块+T实例的合并内存,我们可以实现一个适配分配器,根据实际请求的字节大小,动态选择或创建对应尺寸的boost::pool_allocator实例:

#include <memory>
#include <unordered_map>
#include <mutex>
#include <boost/pool/pool_alloc.hpp>

template <typename T>
struct PoolAllocatorAdapter {
    using value_type = T;

    PoolAllocatorAdapter() = default;
    template <typename U>
    PoolAllocatorAdapter(const PoolAllocatorAdapter<U>&) noexcept {}

    T* allocate(std::size_t n) {
        if (n != 1) { // allocate_shared仅会请求单个对象的分配
            throw std::bad_alloc();
        }
        static std::mutex mtx;
        static std::unordered_map<std::size_t, boost::pool_allocator<T>> pools;

        std::lock_guard<std::mutex> lock(mtx);
        auto& pool = pools[sizeof(T)];
        return pool.allocate(1);
    }

    void deallocate(T* ptr, std::size_t n) noexcept {
        if (n != 1) return;
        std::lock_guard<std::mutex> lock(mtx);
        auto it = pools.find(sizeof(T));
        if (it != pools.end()) {
            it->second.deallocate(ptr, 1);
        }
    }
};

template <typename T, typename... Args>
std::shared_ptr<T> make_shared_with_pool(Args&&... args) {
    return std::allocate_shared<T>(PoolAllocatorAdapter<std::byte>(), std::forward<Args>(args)...);
}

// 使用示例
struct Foo {};
std::shared_ptr<Foo> create() {
    return make_shared_with_pool<Foo>();
}

适配层用std::byte作为值类型,让分配请求的大小直接对应allocate_shared需要的总字节数,内部通过哈希表管理不同尺寸的内存池实例。

2. 预估算合并分配的内存尺寸(仅针对特定实现)

虽然标准未定义控制块的结构,但如果你的代码仅针对特定编译器/标准库,可以估算allocate_shared需要的总内存大小,提前创建对应尺寸的boost::pool_allocator:

#include <type_traits>
#include <boost/pool/pool_alloc.hpp>
#include <memory>

struct Foo {};

// 估算控制块+Foo的总大小(需根据目标标准库调整额外开销)
using AllocType = std::aligned_storage_t<
    sizeof(Foo) + 64, // 64为控制块近似额外开销,GCC/libstdc++、Clang/libc++可能不同
    std::max(alignof(Foo), alignof(std::shared_ptr<void>))
>;

std::shared_ptr<Foo> create() {
    static boost::pool_allocator<AllocType> alloc;
    return std::allocate_shared<Foo>(alloc);
}

注意:这种方法完全不可移植,更换标准库或编译器版本后必须重新调整额外开销值。

3. 手动分配+自定义删除器(放弃内存合并优化)

如果可以接受控制块与T实例分开分配,直接用boost::pool_allocator分配T,再为std::shared_ptr指定自定义删除器完成内存释放:

#include <boost/pool/pool_alloc.hpp>
#include <memory>

struct Foo {};

std::shared_ptr<Foo> create() {
    static boost::pool_allocator<Foo> alloc;
    Foo* ptr = alloc.allocate(1);
    try {
        std::construct_at(ptr);
        return std::shared_ptr<Foo>(ptr, [](Foo* p) {
            std::destroy_at(p);
            alloc.deallocate(p, 1);
        });
    } catch (...) {
        alloc.deallocate(ptr, 1);
        throw;
    }
}

这种方式让boost::pool_allocator完全发挥固定尺寸块的优化优势,但失去了allocate_shared的内存合并(减少内存碎片)特性。


关于标准合规的分配大小获取

C++标准将std::allocate_shared的内部控制块结构列为实现细节,没有任何标准接口可以直接获取其合并分配的内存大小。所有依赖特定标准库内部类型(如GCC的_Sp_counted_ptr_inplace、Clang的__shared_ptr_emplace::_Storage)的方法均属于未定义行为,完全不可移植,严禁在生产代码中使用。


内容的提问来源于stack exchange,提问作者Lukas Barth

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 11:42:01