C++对象池,它以智能指针的形式提供项目,这些项目在删除时返回到池中
C++ object-pool that provides items as smart-pointers that are returned to pool upon deletion
我对c ++的想法很感兴趣,但对这个问题有点困扰。
我想要一个管理资源池的LIFO
类。当请求资源(通过 acquire()
)时,它会将对象作为unique_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())
基本实现,它将通过测试,除了重要的最终测试:
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_;
};
问题:如何使acquire()
返回一个类型的unique_ptr
,以便删除程序知道this
,并调用this->add(...)
,将资源返回到池中。
朴素实现
该实现将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_;
};
用法示例:
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
您可能在此实现中遇到了严重的问题。以下用法并非不可想象:
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
我们需要一种方法来保持删除者进行区分所需的信息
。- 我应该将对象返回到池吗?
- 我应该删除实际对象吗?
一种方法(由 T.C. 建议)是让每个删除程序保留一个weak_ptr
以shared_ptr
SharedPool
中的成员。这会让删除程序知道池是否已被销毁。
正确实现:
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_;
};
下面是一个自定义删除程序,用于检查池是否仍处于活动状态。
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>>();
虽然这个问题很旧并且已经得到了回答,但我对@swalog提出的解决方案有一个小评论。
删除器函子可能会由于双重删除而导致内存损坏:
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);
}
捕获异常时,此处创建的unique_ptr
将被销毁。因此
std::default_delete<T>{}(ptr);
将导致双重删除。
可以通过更改从 T* 创建unique_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(...) {}
}
}
考虑改用shared_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_;
};
相关文章:
- 从链接列表c++中删除一个项目
- 你能检查一下为什么在这个代码中从链接列表中删除项目不起作用吗
- 如何自动获取我的项目的路径并删除一些文件
- 如何从 CLion 的运行窗口中删除程序项目路径
- 类(可能是代理)的命名,允许在不修改基础容器的情况下对项目进行排序和删除
- C++ 从链表中删除项目时出现问题
- 仅从无序集合中删除一个项目
- Visual studio安装项目删除以前的版本
- 如何使用擦除和迭代器来删除二维向量中的项目
- 从 std::vector 中删除项目时"Iterator not incrementable"
- C++ priority_queue与自定义比较器并删除任何项目
- 从 QTreeView 中删除项目时取消选择所有行
- 如果为空,则从 QListWidget 中删除可编辑项目
- 从传递给新线程C++的矢量中删除项目
- C++ 链表删除了错误的项目
- 删除并清除组合框 Win32 API 中的所有项目
- 删除 iOS 项目中的 SDLKey 引用
- 如何将项目属性设置为删除警告MSB8004:中间目录不会以落后的斜线结束
- 如何创建一个函数,该函数可以从一个qtreewidget中删除项目并将其添加到另一个函数
- 在拖放 QML 列表中插入/删除项目使用 cpp 模型查看