C ++对象池,它提供作为智能指针的项,在删除后返回到池 [英] C++ object-pool that provides items as smart-pointers that are returned to pool upon deletion

查看:173
本文介绍了C ++对象池,它提供作为智能指针的项,在删除后返回到池的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述



我想要一个 LIFO 管理资源池的类。
当请求资源时(通过 acquire()),它返回对象作为 unique_ptr



单元测试将是:

  //创建池(为简单起见,int对象)
SharedPool< int>池;
TS_ASSERT(pool.empty());

//添加一个对象到池,现在不再为空
pool.add(std :: unique_ptr< int>(new int(42)));
TS_ASSERT(!pool.empty());

//在自己的范围内调用此对象,使池为空
{
auto v = pool.acquire();
TS_ASSERT_EQUALS(* v,42);
TS_ASSERT (pool.empty());
}

//对象应该已经返回到池
TS_ASSERT(!pool.empty())






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

 模板< class T> 
类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_;
};






问题:如何去使得 acquire()返回类型的 unique_ptr ,使得删除者知道 this ,并调用 this-> add(...),将资源返回到池。 >

解决方案

原生实现



实现使用 unique_ptr 使用自定义删除器将对象返回到池。 获取和发布都是 O(1)。此外,自定义删除器的 unique_ptr 可以隐式转换为 shared_ptr

 模板< 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& );
});
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_;
};

用法示例

  SharedPool< int>池; 
pool.add(std :: unique_ptr< int>(new int(42)));
pool.add(std :: unique_ptr< int>(new int(84)));
pool.add(std :: unique_ptr< int>(new int(1024)));
pool.add(std :: unique_ptr< int>(new int(1337))));

//三种表达unique_ptr对象的方法
auto v1 = pool.acquire();
SharedPool< int> :: ptr_type v2 = pool.acquire();
std :: unique_ptr< int,std :: function< void(int *)> > v3 = pool.acquire();

//使用正确的删除程序隐式转换shared_ptr
std :: shared_ptr< int> v4 = pool.acquire();

//注意,添加一个获取的对象是(正确地)不允许的:
// pool.add(v1); //编译器错误






这个实现的问题。以下用法是不可想象的:

  std :: unique_ptr< SharedPool< Widget> > pool(new SharedPool< Widget>); 
pool-> add(std :: unique_ptr< Widget>(new Widget(42)));
pool-> add(std :: unique_ptr< Widget>(new Widget(84)));

// [Widget,42] acquire(),并从池中释放
auto v1 = pool-> acquire

// [Widget,84]与pool
pool.reset(nullptr)一起被正确销毁;

// [Widget,42]不会被销毁,池不再存在。
v1.reset(nullptr);
//内存泄漏







  1. 我应该将对象返回到池吗?

  2. 我应该删除实际对象吗?

SharedPool 中的 weak_ptr 到 shared_ptr 成员。



正确的实现:



  template< class T> 
class SharedPool
{
private:
struct External_Deleter {
explicit External_Deleter(std :: weak_ptr< SharedPool< T> * pool)
: pool_(pool){}

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);
}
private:
std :: weak_ptr< SharedPool< T> *> pool_;
};

public:
using ptr_type = std :: unique_ptr< T,External_Deleter> ;;

SharedPool():this_ptr_(new SharedPool< T> *(this)){}
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(),
External_Deleter {std :: weak_ptr< SharedPool< T> * {this_ptr_}});
pool_.pop();
return std :: move(tmp);
}

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

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

private:
std :: shared_ptr< SharedPool< T> *> this_ptr_;
std :: stack< std :: unique_ptr< T> > pool_;
};


I'm having fun with c++-ideas, and got a little stuck with this problem.

I would like a LIFO class that manages a pool of resources. When a resource is requested (through acquire()), it returns the object as a unique_ptr that, upon deletion, causes the resource to be returned to the pool.

The unit tests would be:

// 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())


Basic implementation, which would pass the tests, except for the important final test:

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_;
};


The question: How to go about so that acquire() returns a unique_ptr of a type such that the deleter has knowledge of this, and calls this->add(...), returning the resource back to the pool.

解决方案

Naive implementation

The implementation uses unique_ptr with a custom deleter that returns objects to the pool. Both acquire and release are O(1). Additionally, unique_ptr with custom deleters can be implicitly converted to 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_;
};

Example usage:

SharedPool<int> pool;
pool.add(std::unique_ptr<int>(new int(42)));
pool.add(std::unique_ptr<int>(new int(84)));
pool.add(std::unique_ptr<int>(new int(1024)));
pool.add(std::unique_ptr<int>(new int(1337)));

// Three ways to express the unique_ptr object
auto v1 = pool.acquire();
SharedPool<int>::ptr_type v2 = pool.acquire();    
std::unique_ptr<int, std::function<void(int*)> > v3 = pool.acquire();

// Implicitly converted shared_ptr with correct deleter
std::shared_ptr<int> v4 = pool.acquire();

// Note that adding an acquired object is (correctly) disallowed:
// pool.add(v1);  // compiler error


You might have caught a severe problem with this implementation. The following usage isn't unthinkable:

  std::unique_ptr< SharedPool<Widget> > pool( new SharedPool<Widget> );
  pool->add(std::unique_ptr<Widget>(new Widget(42)));
  pool->add(std::unique_ptr<Widget>(new Widget(84)));

  // [Widget,42] acquired(), and released from pool
  auto v1 = pool->acquire();

  // [Widget,84] is destroyed properly, together with pool
  pool.reset(nullptr);

  // [Widget,42] is not destroyed, pool no longer exists.
  v1.reset(nullptr);
  // Memory leak


We need a way to keep alive information necessary for the deleter to make the distinction

  1. Should I return object to pool?
  2. Should I delete the actual object?

One way of doing this (suggested by T.C.), is having each deleter keep a weak_ptr to shared_ptr member in SharedPool. This lets the deleter know if the pool has been destroyed.

Correct implementation:

template <class T>
class SharedPool
{
 private:
  struct External_Deleter {
    explicit External_Deleter(std::weak_ptr<SharedPool<T>* > pool)
        : pool_(pool) {}

    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);
    }
   private:
    std::weak_ptr<SharedPool<T>* > pool_;
  };

 public:
  using ptr_type = std::unique_ptr<T, External_Deleter >;

  SharedPool() : this_ptr_(new SharedPool<T>*(this)) {}
  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(),
                 External_Deleter{std::weak_ptr<SharedPool<T>*>{this_ptr_}});
    pool_.pop();
    return std::move(tmp);
  }

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

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

 private:
  std::shared_ptr<SharedPool<T>* > this_ptr_;
  std::stack<std::unique_ptr<T> > pool_;
};

这篇关于C ++对象池,它提供作为智能指针的项,在删除后返回到池的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆