對象池為了避免頻繁創建耗時或耗資源的大對象,事先在對象池中創建好一定數量的大對象,然後盡量復用對象池中的對象,用戶用完大對象之後放回對象池。
目前縱觀主流語言的實現方式無外乎3個步驟:
一般情況下這樣是OK的,可能存在的問題是在第三步,有兩個問題:
解決顯式回收的問題,實現自動回收,省心省力。
借助c++智能指針,因為智能指針可以自定義刪除器,在智能指針釋放的時候會調用刪除器,在刪除器中我們將用完的對象重新放回對象池。思路比較簡單,但實現的時候需要考慮兩個問題:
自定義刪除器只做一件事,就是將對象重新放入對象池。如果對象池初始化的時候就自定義刪除器的話,刪除器中的邏輯是將對象放回對象池,放回的時候無法再定義一個這樣的刪除器,所以這種做法行不通。
需要注意,回收的對象只能是默認刪除器的。除了前述原因之外,另外一個原因是對象池釋放的時候需要釋放所有的智能指針,釋放的時候如果存在自定義刪除器將會導致對象無法刪除。
只有在get的時候定義刪除器才行,但是初始創建或加入的智能指針是默認刪除器,所以我們需要把智能指針的默認刪除器改為自定義刪除器。
因為我們需要把智能指針的默認刪除器改為自定義刪除器,用shared_ptr會很不方便,因為你無法直接將shared_ptr的刪除器修改為自定義刪除器,雖然你可以通過重新創建一個新對象,把原對象拷貝過來的做法來實現,
但是這樣做效率比較低。而unique_ptr由於是獨占語義,提供了一種簡便的方法方法可以實現修改刪除器,所以用unique_ptr是最適合的。
#pragma once
#include <memory>
#include <vector>
#include <functional>
template <class T>
class SimpleObjectPool
{
public:
using DeleterType = std::function<void(T*)>;
void add(std::unique_ptr<T> t)
{
pool_.push_back(std::move(t));
}
std::unique_ptr<T, DeleterType> get()
{
if (pool_.empty())
{
throw std::logic_error("no more object");
}
//every time add custom deleter for default unique_ptr
std::unique_ptr<T, DeleterType> ptr(pool_.back().release(), [this](T* t)
{
pool_.push_back(std::unique_ptr<T>(t));
});
pool_.pop_back();
return std::move(ptr);
}
bool empty() const
{
return pool_.empty();
}
size_t size() const
{
return pool_.size();
}
private:
std::vector<std::unique_ptr<T>> pool_;
};
//測試代碼:
void test_object_pool()
{
SimpleObjectPool<A> p;
p.add(std::unique_ptr<A>(new A()));
p.add(std::unique_ptr<A>(new A()));
{
auto t = p.get();
p.get();
}
{
p.get();
p.get();
}
std::cout << p.size() << std::endl;
}
如果你堅持用shared_ptr,那麼回收的時候你需要這樣寫:
std::shared_ptr<T> get()
{
if (pool_.empty())
{
throw std::logic_error("no more object");
}
std::shared_ptr<T> ptr = pool_.back();
auto p = std::shared_ptr<T>(new T(*ptr.get()), [this](T* t)
{
pool_.push_back(std::shared_ptr<T>(t));
});
//std::unique_ptr<T, DeleterType> ptr(pool_.back().release(), [this](T* t)
//{
// pool_.push_back(std::unique_ptr<T>(t));
//});
pool_.pop_back();
return p;
}
這種方式需要每次都創建一個新對象,並且拷貝原來的對象,是一種比較低效的做法。
凡是需要自動回收的場景下都可以使用這種方式:在獲取對象的時候將默認刪除器改為自定義刪除器,確保它可以回收。注意,回收的智能指針使用的是默認刪除器,可以確保對象池釋放時能正常釋放對象。同時也將獲取對象和釋放對象時,對象的控制權完全分離。
其他的一些應用場景:多例模式,無需手動釋放,自動回收。