微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

在 C++ 中正确的对象池实现?

如何解决在 C++ 中正确的对象池实现?

我一直试图找到一种方法来在我的项目中实现对象池。我在互联网上找到了一些例子,其中大部分使用 std::lists 和书中的一个例子;也使用 std::list 的游戏开发模式和最佳实践。

不过,有人告诉我,将列表用于对象池是毫无意义的,因为将对象返回到池中意味着必须再次分配内存。

我也看到有人使用 std::stack,但我认为存在同样的问题。

这是我按照本书示例为对象池类编写的代码

    /* This object has only an int data type (value).
       One method to se it and another one to print it. */
class Object
{
    public: 

        Object()
        {
            SetValue();
            std::cout << "Constructed/Reset." << std::endl;
        }

            // copy constructor
        Object( Object& other )
        {
            this->mValue = other.mValue;
            std::cout << "Constructed." << std::endl;
        }

        ~Object()
        {
            std::cout << "Destroyed." << std::endl;
        }

        void SetValue( int val = -1 )
        {
            mValue = val;
        }

        void PrintValue()
        {
            std::cout << this->mValue << std::endl;
        }

    private:
        int mValue;

};

class Pool
{
    public:
        Pool()
        {
            std::cout << "Pool created!\n";
        }

        ~Pool()
        {
            std::cout << "Pool destroyed!\n";
        }

        void Fill( int size )
        {
            for( int i = 0; i < size; i++ )
            {
                Object* obj = new Object();
                pool.push_back( obj );
            }
            std::cout << "Objects created!" << "\nObjects in pool: "
                      << pool.size() << std::endl;
        }

        Object* AquireObject()
        {
            if( !pool.empty() )
            {
                Object* obj = pool.back();
                pool.pop_back();
                
                std::cout << "Object aquired!"
                          << "\nNumber of objects in pool: " << pool.size() << std::endl;

                return obj;
            }
            else
            {
                std::cout << "Object created and aquired!"
                          << "\nNumber of objects in pool: " << pool.size() << std::endl;
                return new Object();
            }
        }

        void ReleaSEObject( Object* returningObject )
        {
            returningObject->SetValue();
            pool.push_back( returningObject );
            std::cout << "Object returned to the pool!"
                      << "\nNumber of objects in pool: " << pool.size() << std::endl;
        }

        void Clear()
        {
            while( !pool.empty() )
            {
                Object* obj = pool.back();
                pool.pop_back();
                delete obj;
            }
            std::cout << "Objects deleted!"
                      << "\nNumber of objects in pool: " << pool.size() << std::endl;
        }

    private:
        std::list<Object*> pool;
};

我也做了一个稍微不同的版本,它使用唯一的指针:

    /* This object has only an int data type (value).
       One method to se it and another one to print it. */
class Object
{
    public: 

        Object()
        {
            SetValue();
            std::cout << "Constructed/Reset." << std::endl;
        }

            // copy constructor
        Object( Object& other )
        {
            this->mValue = other.mValue;
            std::cout << "Constructed." << std::endl;
        }

        ~Object()
        {
            std::cout << "Destroyed." << std::endl;
        }

        void SetValue( int val = -1 )
        {
            mValue = val;
        }

        void PrintValue()
        {
            std::cout << this->mValue << std::endl;
        }

    private:
        int mValue;

};

    // Object pool using std::list
class Pool
{
    public:
        Pool() = default;
        ~Pool() = default;

            /* Fills the pool (list) with a given size using an Object class (prevIoUsly defined)
               as a reference using its copy constructor.*/
        void Fill( int size,Object* obj )
        {
            for( int i = 0; i < size; i++ )
            {
                std::unique_ptr<Object> object = std::make_unique<Object>( *obj );
                pool.push_back( std::move( object ) );
            }
        }

            // Clears all items in the list.
        void PoolIsClosed()
        {
            pool.clear();
        }

            // Returns an instance of an object within the list.
        std::unique_ptr<Object> Getobject()
        {
            if( !pool.empty() )
            {
                std::unique_ptr<Object> object = std::move( pool.front() );
                pool.pop_front();
                
                std::cout << "Object retrieved." << "\nObjects in pool: "
                          << pool.size() << std::endl;
                
                return object;
            }
            else
            {
                /* "WARNING: NOT ALL CONTROL PATHS RETURN A VALUE." */
                std::cout << "Pool is empty." << std::endl;
            }
        }

            // Returns an instance back to the object list.
        void returnToPool( std::unique_ptr<Object> returningObject )
        {
            pool.push_back( std::move( returningObject ) );
            
            std::cout << "Object returned." << "\nObjects in pool: "
                      << pool.size() << std::endl;
        }

    private:
        std::list<std::unique_ptr<Object>> pool;
};

我想知道这样的实现是否真的毫无意义,如果是,是否有一种简单的方法来实现基本的对象池类?

我知道boost库有自己的对象池实现……你们有没有人使用过它?我想了解更多,难用吗?..

我还发现了这个对象池库:https://github.com/mrDIMAS/SmartPool,它看起来很有前途,而且很容易使用,但我不知道它到底是如何工作的,我理解起来有点复杂。对此有何意见?

解决方法

如果你不介意滚动你自己的链表逻辑,你可以创建一个不使用任何数据结构的 ObjectPool 类,因此保证不访问堆(除非它的空闲列表是空,它必须这样做才能返回一个新对象,当然)。像这样(警告,几乎没有测试过的代码,可能包含错误):

#include <stdio.h>

template <typename Object> class ObjectPool
{
public:
   ObjectPool() : _head(NULL),_tail(NULL) {/* empty */}

   ~ObjectPool()
   {
      while(_head)
      {
         ObjectNode * next = _head->_next;
         delete _head;
         _head = next;
      }
   }

   Object * AcquireObject()
   {
      if (_head)
      {
         ObjectNode * oldNode = _head;  // what we're going to return to the user

         // Pop (oldNode) off the front of the free-list
         if (_head == _tail) _head = _tail = NULL;
         else
         {
            _head = _head->_next;
            if (_head) _head->_prev = NULL;
         }
         return &oldNode->_object;
      }
      else
      {
         // There's nothing in our free-list to re-use!  Hand the user a new object instead.
         ObjectNode * newNode = new ObjectNode;
         return &newNode->_object;
      }
   }

   // Note:  (obj) *must* be a pointer that we previously returned via AcquireObject()
   void ReleaseObject(Object * obj)
   {
      // reset (obj) back to its default-constructed state
      // (this might not be necessary,depending what your Objects represent)
      (*obj) = Object();

      ObjectNode * node = reinterpret_cast<ObjectNode *>(obj);   // this works only because _object is the first item in the ObjectNode struct
      if (_head)
      {
         // Prepend our node back to the start of our free-nodes-list
         node->_prev = NULL;
         node->_next = _head;
         _head->_prev = node;
         _head = node;
      }
      else
      {
         // We were empty; now will have just this one ObjectNode in the free-list
         _head = _tail = node;
         node->_prev = node->_next = NULL;
      }
   }

private:
   struct ObjectNode
   {
      Object _object;     // note:  this MUST be the first member-variable declared in the ObjectNode struct!
      ObjectNode * _prev;
      ObjectNode * _next;
   };

   ObjectNode * _head;  // first item in our free-nodes-list,or NULL if we have no available free nodes
   ObjectNode * _tail;  // last item in our free-nodes-list,or NULL if we have no available free nodes
};

// unit test
int main(int,char **)
{
   ObjectPool<int> test;
   int * i = test.AcquireObject();
   printf("i=%p\n",i);
   test.ReleaseObject(i);
   return 0;
}

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。