是否可以使用shared_ptr的对象池模式?

时间:2015-06-27 15:23:40

标签: c++ c++11 shared-ptr object-pooling

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

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

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

3 个答案:

答案 0 :(得分:2)

经过详尽的研究和测试后,我得出结论认为,有一个 no 合法的方式(从C ++ 11或更低版本开始)来创建一个可重用的shared_ptr<T>&#39;直接当然,人们可以很容易地创建一个T个对象池,这些对象可以为shared_ptr<T>提供服务,但这会导致与控制块的每个服务进行堆分配。

然而, 可以间接地创建shared_ptr<T>的对象池(这是我发现的唯一方式它)。通过间接,我的意思是必须实现一个自定义的内存池&#39;样式分配器,用于存储以便在shared_ptr<T>控制块被销毁时释放的内存。然后将此分配器用作`shared_ptr&#39;的第三个参数。构造:

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

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

答案 1 :(得分:0)

是的,这是可能的。但是,我会考虑让它返回boost::intrusive_ptr<T>,而不是让您的池返回std::shared_ptr<T>。您可以intrusive_ptr_release()负责从池中释放该块,然后由您的用户构建T以便您可以创建intrusive_ptr<T>

答案 2 :(得分:0)

您可以将对象存储在池中(例如,作为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); }内部通过alloc()隐式发生。)