2

是否可以创建 shared_ptr 的对象池?在我的脑海中画出这个草图,我可以看到两种方法,但每种方法都有一个缺陷:

  1. 如果 T 个对象存储在可重用池中,则在 get() 请求上将 T 包装在 shared_ptr 中的行为将导致每次在堆上重新分配控制块 - 因此打破了对象池的概念。

  2. 如果 shared_ptr 对象存储在可重用池中,则 shared_ptr 对象必须停止存在才能启动自定义删除器,并且自定义删除器函数仅使用 T 指针调用。所以没有什么可回收的。

4

3 回答 3

3

经过详尽的研究和测试,我得出结论,没有合法的方法(从 C++11 或更低版本开始)可以shared_ptr<T>直接创建可重用的对象池。T当然,可以很容易地创建一个对象池来服务shared_ptr<T>'s,但这会导致堆分配与控制块的每个服务。

然而,可以间接地创建一个对象池(这是我发现的唯一方法)。间接地,我的意思是必须实现一个自定义的“内存池”样式分配器来存储以重用控制块被销毁时释放的内存。这个分配器然后用作 `shared_ptr' 构造函数的第三个参数:shared_ptr<T>shared_ptr<T>

template< class Y, class Deleter, class Alloc > 
   std::shared_ptr( Y* ptr, Deleter d, Alloc alloc );

仍然会使用shared_ptr<T>堆内存来构造/分配和删除/取消分配 - 没有办法阻止它 - 但是通过自定义分配器使内存可重用,可以实现确定性的内存占用。

于 2015-07-22T09:23:10.887 回答
1

是的,这是可能的。但与其让你的游泳池回归std::shared_ptr<T>,我会考虑让它回归boost::intrusive_ptr<T>。您可能intrusive_ptr_release()负责从池中释放该块,然后由您的用户来构建T以便您可以制作intrusive_ptr<T>.

于 2015-06-27T15:26:30.517 回答
1

您可以将对象存储在池中(例如,作为 unique_ptr)。池根据请求返回一个 shared_ptr。自定义删除器将数据返回到池中。此处概述了一个简单的示例:

#ifndef __POOL_H_
#define __POOL_H_

#include <list>
#include <mutex>
#include <algorithm>
#include <memory>
#include <stdexcept>

namespace common {

template<class T, bool grow_on_demand=true>
class Pool 
{
    public:
    Pool(const char* name_p, size_t n) 
        : mutex_m(), free_m(0), used_m(0), name_m(name_p) 
    {
        for (size_t i=0; i<n; i++)
        {
           free_m.push_front( std::make_unique<T>() );
        }
    }

    const char* getName() const
    {
        return name_m.c_str();
    }

    std::shared_ptr<T> alloc()
    {
        std::unique_lock<std::mutex> lock(mutex_m);
        if (free_m.empty() )
        {
            if constexpr (grow_on_demand) 
            {
                free_m.push_front( std::make_unique<T>() );
            }
            else
            {
                throw std::bad_alloc();
            }
        }
        auto it = free_m.begin();
        std::shared_ptr<T> sptr( it->get(), [=](T* ptr){ this->free(ptr); } );
        used_m.push_front(std::move(*it));
        free_m.erase(it);
        return sptr;
    }


    size_t getFreeCount()
    {
        std::unique_lock<std::mutex> lock(mutex_m);
        return free_m.size();
    }

private:

    void free(T *obj)
    {
        std::unique_lock<std::mutex> lock(mutex_m);
        auto it = std::find_if(used_m.begin(), used_m.end(), [&](std::unique_ptr<T> &p){ return p.get()==obj; } );
        if (it != used_m.end())
        {
          free_m.push_back(std::move(*it));
          used_m.erase(it);
        }
        else
        {
            throw std::runtime_error("unexpected: unknown object freed.");
        }
    }

    std::mutex mutex_m;
    std::list<std::unique_ptr<T>> free_m;
    std::list<std::unique_ptr<T>> used_m;
    std::string name_m;
};

}

#endif /* __POOL_H_ */

默认情况下,如果您从空池 (grow_on_demand=true) 分配新对象,则池会添加新项目。

  • 一个新的池创建n元素并将它们添加到池中(使用默认构造函数)。
  • 您可以从mypool.alloc()池中获取对象。
  • 当分配的对象不再使用时,shared_ptr 会自动返回到池中(通过[=](T* ptr){ this->free(ptr); }from within隐式发生alloc()
于 2019-01-04T18:09:00.050 回答