【转载】C++11 实现一个超级对象池

1,416 阅读3分钟

参考原文地址:(原创) 一个超级对象池的实现

我对文章的格式和错别字进行了调整,并补充标注出了重要的部分,代码也增加了详细注释。以下是正文。

正文

对象池对于创建开销比较大的对象来说很有意义,为了避免重复创建开销比较大的对象,我们可以通过对象池来优化。

对象池的思路比较简单,事先创建好一批对象,放到一个集合中,以后每当程序需要新的对象时候,都从对象池里获取,每当程序用完该对象后,都把该对象归还给对象池。这样会避免重复的对象创建,提高程序性能。

先来看看对象池的简单实现:

简单对象池的实现

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

这个 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();
        
        /// @note 用函数对象封装
        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);
    }
    
    /// @note 返回智能指针
    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)
                                  {///< Deleter 回收器
                                      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)
        {
            /// @note 取得范围中满足类型条件的函数对象
            /// 继而利用它获取(或创建)对象指针
            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>();
        /// @note 返回智能指针
        return createPtr<T, Args...>(constructName, args...);
    }
    
    /// @note 初始化对象池
    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;
        }
    }

    /// @note 从对象池中获取对象
    template <typename T, typename... Args>
    std::shared_ptr<T> GetInstance(std::string &constructName, Args... args)
    {
        /// @note 寻找对象池中是否已经存有该对象
        auto it = m_object_map.find(constructName);
        if (it == m_object_map.end())
            return nullptr;
        
        /// @note 取出并转型该指针
        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();
}