对象池的实现
对象池:在一部分内存空间(池子)中事先实例化好固定数量的对象,当需要使用池中的对象时,首先判断该池中是否有闲置(暂未使用)的对象,如果有,则取出使用,如果没有,则在池中创建该对象。当一个对象不再被使用时,其应该将其放回对象池,以便后来的程序使用。
我们来看实例了解一下为什么需要对象池呢?
我们先来看一下这样一个例子:
//Author:Mr.Rain
#include <iostream>
using namespace std;
template <typename T>
class Queue
{
public:
Queue()
{
_front = _rear = new QueueItem();
}
~Queue()
{
QueueItem *cur = _front;
while (cur != nullptr)
{
_front = _front->_next;
delete cur;
cur = _front;
}
}
void push(const T &val)//入队
{
QueueItem *item = new QueueItem(val);
_rear->_next = item;
_rear = item;
}
void pop()//出队
{
if (empty()) return;
QueueItem *first = _front->_next;
_front->_next = first->_next;
if (_front->_next == nullptr)
{
_rear = _front;
}
delete first;
}
T front()const
{
return _front->_next->_data;
}
bool empty()const
{
return _front == _rear;
}
private:
struct QueueItem
{
QueueItem(T data = T()):_data(data), _next(nullptr){}
T _data;
QueueItem *_next;
};
QueueItem *_front;//队头
QueueItem *_rear;//队尾
};
int main()
{
Queue<int> que;
for (int i=0; i<1000000; ++i)
{
que.push(i);
que.pop();
}
cout << que.empty() << endl;
return 0;
}
这里我们实现了一个队列并执行成功,入队一个出队一个,最后队列为空。
但是我们每次push都会执行一次new QueueItem,每次pop都会执行一次QueueItem的delete;对于我们结点QueueItem来说,将其内存释放掉,下一次push时又需要一个新的QueueItem结点,与原来不同的是只是结点中数据不同,短时间内大量对其进行调用,完全没有必要,会影响我们程序的性能。
因此我们产生一个QueueItem的对象池,在对象池中生成100000QueueItem结点的对象池,需要一个QueueItem直接从对象池中拿,delete时将结点内存归还到内存池中,短时间对QueueItem开辟释放开辟释放性能会有很大提升。
此时我们利用new与delete针对于QueueItem设计一个对象池:
对象池大致结构如下:
代码实现:
//Author:Mr.Rain
#include <iostream>
using namespace std;
template <typename T>
class Queue
{
public:
Queue()
{
_front = _rear = new QueueItem();
}
~Queue()
{
QueueItem *cur = _front;
while (cur != nullptr)
{
_front = _front->_next;
delete cur;
cur = _front;
}
}
void push(const T &val)//入队
{
QueueItem *item = new QueueItem(val);
_rear->_next = item;
_rear = item;
}
void pop()//出队
{
if (empty()) return;
QueueItem *first = _front->_next;
_front->_next = first->_next;
if (_front->_next == nullptr)
{
_rear = _front;
}
delete first;
}
T front()const
{
return _front->_next->_data;
}
bool empty()const
{
return _front == _rear;
}
private:
//产生一个QueueItem的对象池(10000个QueueItem结点)
struct QueueItem
{
QueueItem(T data = T()):_data(data), _next(nullptr){}
//给QueueItem提供自定义内存管理
void* operator new(size_t size)
{
if (_itemPool == nullptr)
{
_itemPool = (QueueItem*)new char[POOL_ITEM_SIZE * sizeof(QueueItem)];
QueueItem *p = _itemPool;//指向首地址
for (; p < _itemPool + POOL_ITEM_SIZE; ++p)
{
p->_next = p + 1;
}
p->_next = nullptr;//最后一个结点地址域nullptr
}
QueueItem *p = _itemPool;
_itemPool = _itemPool->_next;
return p;
}
void operator delete(void *ptr)
{
QueueItem *p = (QueueItem*)ptr;//归还结点
p->_next = _itemPool;
_itemPool = p;
}
T _data;
QueueItem *_next;
static QueueItem *_itemPool;//指向对象池的起始地址
static const int POOL_ITEM_SIZE = 100000;
};
QueueItem *_front;//队头
QueueItem *_rear;//队尾
};
template <typename T>
typename Queue<T>::QueueItem *Queue<T>::QueueItem::_itemPool = nullptr;
int main()
{
Queue<int> que;
for (int i=0; i<1000000; ++i)
{
que.push(i);
que.pop();
}
cout << que.empty() << endl;
return 0;
}
测试一下:测试成功。
由于对象池的实现,提前开辟好了内存池的空间,用的时候直接取,不用的时候直接归还,大大提高了使用的效率。