(原創)一個超級對象池的實現


  對象池對於創建開銷比較大的對象來說很有意義,為了避免重復創建開銷比較大的對象,我們可以通過對象池來優化。對象池的思路比較簡單,事先創建好一批對象,放到一個集合中,以后每當程序需要新的對象時候,都從對象池里獲取,每當程序用完該對象后,都把該對象歸還給對象池。這樣會避免重復的對象創建,提高程序性能。先來看看對象池的簡單實現:

#include <list>
 
template<typename Object>
class ObjectPool
{
public:
 
    ObjectPool(size_t unSize) :
        m_unSize(unSize)
    {
        for (size_t unIdx = 0; unIdx < m_unSize; ++ unIdx)
    {
        m_oPool.push_back(new Object());
    }
    }
 
    ~ObjectPool()
    {
    typename std::list<Object *>::iterator oIt = m_oPool.begin();
    while (oIt != m_oPool.end())
    {
        delete (*oIt);
        ++ oIt;
    }
    m_unSize = 0;
    }
 
    Object * GetObject()
    {
        Object * pObj = NULL;
        if (0 == m_unSize)
        {
            pObj = new Object();
        }
        else
        {
            pObj = m_oPool.front();
            m_oPool.pop_front();
            -- m_unSize;
        }
 
        return pObj;
    }
 
    void ReturnObject(Object * pObj)
    {
        m_oPool.push_back(pObj);
        ++ m_unSize;
    }
 
private:
    size_t m_unSize;
    std::list<object *> m_oPool;
};
View Code

  這個object pool的實現很典型,初始創建一定數量的對象,取的時候就直接從池子中取,用完之后再回收到池子。一般的對象池的實現思路和這個類似,這種實現方式雖然能達到目的,但是存在以下不足:

  1. 對象池ObjectPool<T>只能容納特定類型的對象,不能容納所有類型的對象,可以支持重載的和參數不同的構造函數;
  2. 對象用完之后需要手動回收,用起來不夠方便,更大的問題是存在忘記回收的風險;

  我希望能有一個更強大的對象池,這個對象池能容納所有的對象,還能自動回收用完了對象,不需要手動回收,用起來更方便。要實現這樣的對象池需要解決前面提到的兩個問題,通過c++11就可以解決這兩個問題。

  對於問題1:容納所有的對象。本質上需要將對象池中的對象類型擦除,這里用Any類型就可以解決。

  對於問題2:自動回收用完的對象。這里用智能指針就可以解決,在創建智能指針時可以指定刪除器,在刪除器中不刪除對象,而是回收到對象池中,而這個過程對外界來說是看不見的,由智能指針自己完成。關於Any的實現見我前面的博客內容:c++11打造好用的Any

  下面來看看超級對象池的具體實現吧。

#include <string>
#include <functional>
#include <tuple>
#include <map>

#include "Any.hpp"

const int MaxObjectNum = 10;

class ObjectPool
{
    template<typename T, typename... Args>
    using Constructor = std::function<std::shared_ptr<T>(Args...)>;
public:

    ObjectPool() : needClear(false)
    {
    }

    ~ObjectPool()
    {
        needClear = true;
    }

    //默認創建多少個對象
    template<typename T, typename... Args>
    void Create(int num)
    {
        if (num <= 0 || num > MaxObjectNum)
            throw std::logic_error("object num errer");

        auto constructName = typeid(Constructor<T, Args...>).name();

        Constructor<T, Args...> f = [constructName, this](Args... args)
        {
            return createPtr<T>(string(constructName), args...);
        };

        m_map.emplace(typeid(T).name(), f);

        m_counter.emplace(constructName, num);
    }

    template<typename T, typename... Args>
    std::shared_ptr<T> createPtr(std::string& constructName, Args... args)
    {
        return std::shared_ptr<T>(new T(args...), [constructName, this](T* t)
        {
            if (needClear)
                delete[] t;
            else
                m_object_map.emplace(constructName, std::shared_ptr<T>(t));
        });
    }

    template<typename T, typename... Args>
    std::shared_ptr<T> Get(Args... args)
    {
        using ConstructType = Constructor<T, Args...>;

        std::string constructName = typeid(ConstructType).name();
        auto range = m_map.equal_range(typeid(T).name());

        for (auto it = range.first; it != range.second; ++it)
        {
            if (it->second.Is<ConstructType>())
            {
                auto ptr = GetInstance<T>(constructName, args...);

                if (ptr != nullptr)
                    return ptr;

                return CreateInstance<T, Args...>(it->second, constructName, args...);
            }
        }

        return nullptr;
    }

private:
    template<typename T, typename... Args>
    std::shared_ptr<T> CreateInstance(Any& any,
        std::string& constructName, Args... args)
    {
        using ConstructType = Constructor<T, Args...>;
        ConstructType f = any.AnyCast<ConstructType>();

        return createPtr<T, Args...>(constructName, args...);
    }

    template<typename T, typename... Args>
    void InitPool(T& f, std::string& constructName, Args... args)
    {
        int num = m_counter[constructName];

        if (num != 0)
        {
            for (int i = 0; i < num - 1; i++)
            {
                m_object_map.emplace(constructName, f(args...));
            }
            m_counter[constructName] = 0;
        }
    }

    template<typename T, typename... Args>
    std::shared_ptr<T> GetInstance(std::string& constructName, Args... args)
    {
        auto it = m_object_map.find(constructName);
        if (it == m_object_map.end())
            return nullptr;

        auto ptr = it->second.AnyCast<std::shared_ptr<T>>();
        if (sizeof...(Args)>0)
            *ptr.get() = std::move(T(args...));

        m_object_map.erase(it);
        return ptr;
    }

private:
    std::multimap<std::string, Any> m_map;
    std::multimap<std::string, Any> m_object_map;
    std::map<std::string, int> m_counter;
    bool needClear;
};

測試代碼:

struct AT
{
    AT(){}

    AT(int a, int b) :m_a(a), m_b(b){}

    void Fun()
    {
        cout << m_a << " " << m_b << endl;
    }

    int m_a = 0;
    int m_b = 0;
};

struct BT
{
    void Fun()
    {
        cout << "from object b " << endl;
    }
};

void TestObjectPool()
{
    ObjectPool pool;
    pool.Create<AT>(2);
    pool.Create<BT>(2);
    pool.Create<AT, int, int>(2);

    {
        auto p = pool.Get<AT>();
        p->Fun();
    }
    
    auto pb = pool.Get<BT>();
    pb->Fun();

    auto p = pool.Get<AT>();
    p->Fun();

    int a = 5, b = 6;
    auto p2 = pool.Get<AT>(a, b);
    p2->Fun();

    auto p3 = pool.Get<AT>(3, 4);
    p3->Fun();

    {
        auto p4 = pool.Get<AT>(3, 4);
        p4->Fun();
    }
    auto p4 = pool.Get<AT>(7, 8);
    p4->Fun();
}

測試結果:

  可以看到這個對象池不僅僅能容納所有類型的對象還能自動回收用完的對象,強大而又方便。

 

如果你覺得這篇文章對你有用,可以點一下推薦,謝謝。

c++11 boost技術交流群:296561497,歡迎大家來交流技術。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM