C++ C++;对象池,该对象池将项目作为智能指针提供,删除后返回到池

C++ C++;对象池,该对象池将项目作为智能指针提供,删除后返回到池,c++,c++11,smart-pointers,pool,C++,C++11,Smart Pointers,Pool,我对c++的想法很感兴趣,并且有点被这个问题困住了 我想要一个管理资源池的LIFO类。 当请求资源时(通过acquire()),它将对象作为唯一的\u ptr返回,删除后,该对象将导致资源返回到池中 单元测试将是: // Create the pool, that holds (for simplicity, int objects) SharedPool<int> pool; TS_ASSERT(pool.empty()); // Add an object to the poo

我对c++的想法很感兴趣,并且有点被这个问题困住了

我想要一个管理资源池的
LIFO
类。 当请求资源时(通过
acquire()
),它将对象作为
唯一的\u ptr
返回,删除后,该对象将导致资源返回到池中

单元测试将是:

// Create the pool, that holds (for simplicity, int objects)
SharedPool<int> pool;
TS_ASSERT(pool.empty());

// Add an object to the pool, which is now, no longer empty
pool.add(std::unique_ptr<int>(new int(42)));
TS_ASSERT(!pool.empty());

// Pop this object within its own scope, causing the pool to be empty
{
  auto v = pool.acquire();
  TS_ASSERT_EQUALS(*v, 42);
  TS_ASSERT(pool.empty());
}

// Object should now have returned to the pool
TS_ASSERT(!pool.empty())
//创建池,其中包含(为简单起见,使用int对象)
共享池;
TS_断言(pool.empty());
//将对象添加到池中,该池现在不再为空
pool.add(std::unique_ptr(newint(42));
TS_断言(!pool.empty());
//将此对象弹出到它自己的范围内,导致池为空
{
auto v=pool.acquire();
TS_断言_等于(*v,42);
TS_断言(pool.empty());
}
//对象现在应该已返回池
TS_断言(!pool.empty())

基本实现,将通过测试,重要的最终测试除外:

template <class T>
class SharedPool
{
 public:
  SharedPool(){}
  virtual ~SharedPool(){}

  void add(std::unique_ptr<T> t) {
    pool_.push(std::move(t));
  }

  std::unique_ptr<T> acquire() {
    assert(!pool_.empty());
    std::unique_ptr<T> tmp(std::move(pool_.top()));
    pool_.pop();
    return std::move(tmp);
  }

  bool empty() const {
    return pool_.empty();
  }

 private:
  std::stack<std::unique_ptr<T> > pool_;
};
模板
类SharedPool
{
公众:
SharedPool(){}
虚拟~SharedPool(){}
无效添加(标准::唯一\u ptr t){
池推(标准::移动(t));
}
std::unique_ptr acquire(){
断言(!pool.empty());
std::unique_ptr tmp(std::move(pool_u.top());
pool_uu.pop();
返回标准::移动(tmp);
}
bool empty()常量{
返回池为空();
}
私人:
std::堆栈池;
};


问题:如何执行,以便
acquire()
返回一个类型为
唯一的\u ptr
,这样删除者就知道
,并调用
此->添加(…)
,将资源返回池。

考虑改用
共享的\u ptr
。您需要做的唯一更改是不计算拥有多个所有者的自动指针。从
SharedPool
获取的对象可以正常删除自动指针,但
SharedPool
仍将保留实际的自动指针

template <class T>
class SharedPool
{
 public:
  SharedPool(){}
  virtual ~SharedPool(){}

  void add(std::unique_ptr<T> t) {
    pool_.push_back(std::move(t));
  }

  std::shared_ptr<T> acquire() {
    assert(!empty());
    return *std::find_if(pool_.begin(), pool.end(), [](const std::shared_ptr<T>& i){return i.count() == 1;});
  }

  bool empty() const {
    return std::none_of(pool_.begin(), pool_.end(), [](const std::shared_ptr<T>& i){return i.count() == 1;});
  }

 private:
  std::vector<std::shared_ptr<T>> pool_;
};
模板
类SharedPool
{
公众:
SharedPool(){}
虚拟~SharedPool(){}
无效添加(标准::唯一\u ptr t){
池推回(标准::移动(t));
}
std::共享的获取{
断言(!empty());
return*std::find_if(pool.begin(),pool.end(),[](const std::shared_ptr&i){return i.count()==1;});
}
bool empty()常量{
返回std::none_of(pool.begin()、pool.end()、[](const std::shared_ptr&i){return i.count()==1;});
}
私人:
std::向量池;
};
Naive实现 该实现将
unique_ptr
与一个自定义删除器一起使用,该删除器将对象返回到池中。
acquire
release
都是
O(1)
。此外,带有自定义删除器的
unique_ptr
可以隐式转换为
shared_ptr

template <class T>
class SharedPool
{
 public:
  using ptr_type = std::unique_ptr<T, std::function<void(T*)> >;

  SharedPool() {}
  virtual ~SharedPool(){}

  void add(std::unique_ptr<T> t) {
    pool_.push(std::move(t));
  }

  ptr_type acquire() {
    assert(!pool_.empty());
    ptr_type tmp(pool_.top().release(),
                 [this](T* ptr) {
                   this->add(std::unique_ptr<T>(ptr));
                 });
    pool_.pop();
    return std::move(tmp);
  }

  bool empty() const {
    return pool_.empty();
  }

  size_t size() const {
    return pool_.size();
  }

 private:
  std::stack<std::unique_ptr<T> > pool_;
};

这里有一个自定义删除程序,用于检查池是否仍处于活动状态

template<typename T>
class return_to_pool
{
  std::weak_ptr<SharedPool<T>> pool

public:
  return_to_pool(const shared_ptr<SharedPool<T>>& sp) : pool(sp) { }

  void operator()(T* p) const
  {
    if (auto sp = pool.lock())
    {
      try {
        sp->add(std::unique_ptr<T>(p));
        return;
      } catch (const std::bad_alloc&) {
      }
    }
    std::default_delete<T>{}(p);
  }
};

template <class T>
class SharedPool : std::enable_shared_from_this<SharedPool<T>>
{
public:
  using ptr_type = std::unique_ptr<T, return_to_pool<T>>;
  ...
  ptr_type acquire()
  {
    if (pool_.empty())
      throw std::logic_error("pool closed");
    ptr_type tmp{pool_.top().release(), this->shared_from_this()};
    pool_.pop();
    return tmp;
  }
  ...
};

// SharedPool must be owned by a shared_ptr for enable_shared_from_this to work
auto pool = std::make_shared<SharedPool<int>>();
模板
类返回\u到\u池
{
std::弱ptr池
公众:
返回到池(constshared):池(sp){
void运算符()(T*p)常量
{
if(auto sp=pool.lock())
{
试一试{
sp->add(std::unique_ptr(p));
返回;
}捕获(const std::bad_alloc&){
}
}
std::default_delete{}(p);
}
};
模板
类SharedPool:std::从此\u启用\u共享\u
{
公众:
使用ptr_type=std::unique_ptr;
...
ptr_类型获取()
{
if(池\空()
抛出std::逻辑_错误(“池已关闭”);
ptr_类型tmp{pool_u.top().release(),this->shared_from_this()};
pool_uu.pop();
返回tmp;
}
...
};
//SharedPool必须由共享的\u ptr拥有,才能从\u启用\u共享\u
自动池=标准::使_共享();

虽然这个问题很老,而且已经得到了回答,但我对@swalog提出的解决方案有一点小小的评论

由于双重删除,Deleter functor可能导致内存损坏:

void operator()(T* ptr) {
  if (auto pool_ptr = pool_.lock()) {
    try {
      (*pool_ptr.get())->add(std::unique_ptr<T>{ptr});
      return;
    } catch(...) {}
  }
  std::default_delete<T>{}(ptr);
}
将导致双重删除

它可以通过从T*更改创建唯一\u ptr的位置来修复:

void operator()(T* ptr) {
  std::unique_ptr<T> uptr(ptr);
  if (auto pool_ptr = pool_.lock()) {
    try {
      (*pool_ptr.get())->add(std::move(uptr));
      return;
    } catch(...) {}
  }
}
void操作符()(T*ptr){
标准:唯一的ptr uptr(ptr);
如果(自动池\u ptr=pool\uu.lock()){
试一试{
(*pool_ptr.get())->add(std::move(uptr));
返回;
}捕获(…){}
}
}

如果使用自定义删除器,则不再返回
std::unique\u ptr
。要么修复签名,要么使用类型已擦除的删除器(例如
shared\u ptr
)。我知道:),它可能是
std::unique\u ptr
类型,但我不想添加半个答案。我的困惑更多的是如何将它与
std::bind
正确地结合起来。我将依靠更有经验的C++开发人员来填补空白。我后来想解决的另一个问题是返回一个
std::shared_ptr
,但是,如果对
std::unique_ptr
正确地解决了这个问题,那么对于
shared_ptr
案例,它会自动得到解决。感谢您的建议。唯一的缺点(但很重要的一点)是
acquire
empty
都是
O(n)
,而它们应该是
O(1)
(因为它们可以)。我用我想到的
O(1)
实现添加了一个答案。我会将对象存储在池中,作为普通的
唯一的\u ptr
s(无自定义删除器),并仅在
acquire()
中附加自定义删除程序(可返回带有自定义删除程序的
唯一\u ptr
,或带有类型擦除删除程序的
共享\u ptr
)。这也避免了编写自定义析构函数的需要。
std::function
的构造函数可以抛出,但是
unique\u ptr
要求其删除器的构造函数不能抛出,因此您可能希望使用自定义删除器类型。此外,您的“更健壮”版本本质上使用了引用计数智能指针。标准库中已经有这样一个指针…自定义删除程序仍然需要调用
std::stack::push(unique_ptr&&)
,它可以抛出,如果在template<typename T> class return_to_pool { std::weak_ptr<SharedPool<T>> pool public: return_to_pool(const shared_ptr<SharedPool<T>>& sp) : pool(sp) { } void operator()(T* p) const { if (auto sp = pool.lock()) { try { sp->add(std::unique_ptr<T>(p)); return; } catch (const std::bad_alloc&) { } } std::default_delete<T>{}(p); } }; template <class T> class SharedPool : std::enable_shared_from_this<SharedPool<T>> { public: using ptr_type = std::unique_ptr<T, return_to_pool<T>>; ... ptr_type acquire() { if (pool_.empty()) throw std::logic_error("pool closed"); ptr_type tmp{pool_.top().release(), this->shared_from_this()}; pool_.pop(); return tmp; } ... }; // SharedPool must be owned by a shared_ptr for enable_shared_from_this to work auto pool = std::make_shared<SharedPool<int>>();
void operator()(T* ptr) {
  if (auto pool_ptr = pool_.lock()) {
    try {
      (*pool_ptr.get())->add(std::unique_ptr<T>{ptr});
      return;
    } catch(...) {}
  }
  std::default_delete<T>{}(ptr);
}
std::default_delete<T>{}(ptr);
void operator()(T* ptr) {
  std::unique_ptr<T> uptr(ptr);
  if (auto pool_ptr = pool_.lock()) {
    try {
      (*pool_ptr.get())->add(std::move(uptr));
      return;
    } catch(...) {}
  }
}