是否可以创建 shared_ptr 的对象池?在我的脑海中画出这个草图,我可以看到两种方法,但每种方法都有一个缺陷:
如果 T 个对象存储在可重用池中,则在 get() 请求上将 T 包装在 shared_ptr 中的行为将导致每次在堆上重新分配控制块 - 因此打破了对象池的概念。
如果 shared_ptr 对象存储在可重用池中,则 shared_ptr 对象必须停止存在才能启动自定义删除器,并且自定义删除器函数仅使用 T 指针调用。所以没有什么可回收的。
是否可以创建 shared_ptr 的对象池?在我的脑海中画出这个草图,我可以看到两种方法,但每种方法都有一个缺陷:
如果 T 个对象存储在可重用池中,则在 get() 请求上将 T 包装在 shared_ptr 中的行为将导致每次在堆上重新分配控制块 - 因此打破了对象池的概念。
如果 shared_ptr 对象存储在可重用池中,则 shared_ptr 对象必须停止存在才能启动自定义删除器,并且自定义删除器函数仅使用 T 指针调用。所以没有什么可回收的。
经过详尽的研究和测试,我得出结论,没有合法的方法(从 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>
堆内存来构造/分配和删除/取消分配 - 没有办法阻止它 - 但是通过自定义分配器使内存可重用,可以实现确定性的内存占用。
是的,这是可能的。但与其让你的游泳池回归std::shared_ptr<T>
,我会考虑让它回归boost::intrusive_ptr<T>
。您可能intrusive_ptr_release()
负责从池中释放该块,然后由您的用户来构建T
以便您可以制作intrusive_ptr<T>
.
您可以将对象存储在池中(例如,作为 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()
池中获取对象。[=](T* ptr){ this->free(ptr); }
from within隐式发生alloc()
。