thinking in object pool

1.背景 

 對象池爲了不頻繁建立耗時或耗資源的大對象,事先在對象池中建立好必定數量的大對象,而後儘可能複用對象池中的對象,用戶用完大對象以後放回對象池。c++

2.問題 

 目前縱觀主流語言的實現方式無外乎3個步驟:測試

  1. 初始建立必定數量的對象池(也容許從外面添加對象)。
  2. 從對象池中取對象來使用。
  3. 用完以後返回對象池。

通常狀況下這樣是OK的,可能存在的問題是在第三步,有兩個問題:this

  1. 不方便,每次都須要顯式回收對象。
  2. 忘記將對象放回對象池,形成資源浪費。

3.改進動機 

 解決顯式回收的問題,實現自動回收,省心省力。spa

4.技術內幕

藉助c++智能指針,由於智能指針能夠自定義刪除器,在智能指針釋放的時候會調用刪除器,在刪除器中咱們將用完的對象從新放回對象池。思路比較簡單,但實現的時候須要考慮兩個問題:指針

  1. 何時定義刪除器?
  2. 用shared_ptr仍是unique_ptr? 

4.1何時定義刪除器

  自定義刪除器只作一件事,就是將對象從新放入對象池。若是對象池初始化的時候就自定義刪除器的話,刪除器中的邏輯是將對象放回對象池,放回的時候沒法再定義一個這樣的刪除器,因此這種作法行不通。
須要注意,回收的對象只能是默認刪除器的。除了前述緣由以外,另一個緣由是對象池釋放的時候須要釋放全部的智能指針,釋放的時候若是存在自定義刪除器將會致使對象沒法刪除。
只有在get的時候定義刪除器才行,可是初始建立或加入的智能指針是默認刪除器,因此咱們須要把智能指針的默認刪除器改成自定義刪除器。 code

4.2用shared_ptr仍是unique_ptr

  由於咱們須要把智能指針的默認刪除器改成自定義刪除器,用shared_ptr會很不方便,由於你沒法直接將shared_ptr的刪除器修改成自定義刪除器,雖然你能夠經過從新建立一個新對象,把原對象拷貝過來的作法來實現,
可是這樣作效率比較低。而unique_ptr因爲是獨佔語義,提供了一種簡便的方法方法能夠實現修改刪除器,因此用unique_ptr是最適合的。 對象

4.3實現源碼

#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,那麼回收的時候你須要這樣寫:blog

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

 這種方式須要每次都建立一個新對象,而且拷貝原來的對象,是一種比較低效的作法。 資源

5.總結

  凡是須要自動回收的場景下均可以使用這種方式:在獲取對象的時候將默認刪除器改成自定義刪除器,確保它能夠回收。注意,回收的智能指針使用的是默認刪除器,能夠確保對象池釋放時能正常釋放對象。同時也將獲取對象和釋放對象時,對象的控制權徹底分離。
  其餘的一些應用場景:多例模式,無需手動釋放,自動回收。 get

相關文章
相關標籤/搜索