我对 c++-ideas 很感兴趣,但遇到了这个问题。
我想要 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. 建议)是让每个删除器在 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_;
};
关于C++ 对象池,将项目作为智能指针提供,在删除时返回到池中,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27827923/