5
\$\begingroup\$

The main advantage of this pattern is when I often create and destroy objects, which in this case are often used.

I made this because I needed to track several short timers at the same time, but it can also be used for stuff like missiles or else.

#include <vector>
#include <queue>

#define itv(TYPE) std::vector<TYPE>::iterator

using namespace std;
template <class TYPE>
struct object_pool
{
    std::vector<TYPE> pool;
    std::queue<size_t> avail;
    TYPE & operator[](const size_t & i) { return pool[i]; }
    void add(size_t & pos)
    {
        if(avail.empty()) // no reusable object
        { pool.push_back(TYPE()); pos = pool.size()-1; }
        else
        { pos = avail.back(); avail.pop(); }
    }
    void rem(size_t & a) { avail.push(a); }
    size_t size() { return pool.size(); }
};

What do you think ?

\$\endgroup\$
7
\$\begingroup\$

Don't do this:

using namespace std;

In anything other than a toy this will cause problems.

I don't like this:

#define itv(TYPE) std::vector<TYPE>::iterator

With C++11 this type if thing has been resolved by auto.

I would also define the iterator in terms of the object pool:

template <class TYPE>
struct object_pool
{
    typedef std::vector<TYPE>::iterator  iterator;

I would hide the fact that internally the object_pool uses a vector and queue. I would then add the obligatory functions to extract information:

template <class TYPE>
class object_pool
{
    std::vector<TYPE> pool;
    std::queue<size_t> avail;

  public:
    typedef std::vector<TYPE>::iterator       iterator;
    typedef std::vector<TYPE>::const_iterator const_iterator;

    iterator       begin()       { return pool.begin();}
    iterator       end()         { return pool.end();}
    const_iterator begin() const { return pool.begin();}
    const_iterator end()   const { return pool.end();}

To access elements I would also provide const version

    TYPE&       operator[](std::size_t index)       { return pool[index];}
    TYPE&       at(std::size_t index)               { return pool.at(index);}

    TYPE const& operator[](std::size_t index) const { return pool[index];}
    TYPE const& at(std::size_t index) const         { return pool.at(index);}

The methods that can be const should be const:

    size_t size() const { return pool.size(); }
             //  ^^^^^^^

The implementation details are fine.

    void add(size_t & pos)
    {
        if(avail.empty()) // no reusable object
        { pool.push_back(TYPE()); pos = pool.size()-1; }
        else
        { pos = avail.back(); avail.pop(); }
    }
    void rem(size_t & a) { avail.push(a); }

I would change the add() so it returned pos:

    size_t add();

Also the vector starts off very small and re-sizes to get bigger.
To make sure that it does not re-size too often I would add a constructor that gives the vector a reasonable size to start with:

object_pool::object_pool()
{
    pool.reserve(1000);
}

But I think you are using the wrong technique.
I would use a pool allocator and plug it into the vector.

You can read more about it here: https://stackoverflow.com/q/2984434/14065

\$\endgroup\$
  • \$\begingroup\$ boost seems like a little complicated for what I want to do with do with it. \$\endgroup\$ – jokoon Jul 25 '12 at 16:22
  • 1
    \$\begingroup\$ You don't need to use boost. But implementing an allocator would seem a better solution (and also quicker). Though it adds some extra complexity you probably don't need. You need to way complexity against the added advantage for your particular situation. Note: by learning how to use allocators makes your use of the STL more effective. \$\endgroup\$ – Martin York Jul 25 '12 at 17:44

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.