使用对象池的原因
类似于SGI的二级空间配置器,但是对象池的实现比二级空间配置器实现的原理简单了很多。想了解二级空间配置器可以查看博主的另外一篇文章https://blog.csdn.net/qq_42418668/article/details/94740594
当我们需要,多次使用new和delete操作向系统申请和释放堆空间会使得我们的程序进程的开销变得非常的大而且效率也非常低。
所以我们可以通过重载new和delete构建一个对象池,来帮助我们提高自己程序的效率和减少不必要的开销。
实现原理
-
分配过程(new)
我们首先申请比如100000块内存空间,用这个块类型的指针指向这个申请好内存的首地址。如图。这个块可以是一个链式队列的一个节点或者一个树的节点等。
当我们要分配给用户一块空间的时候我们就可以,先将_itemPool的地址保存到临时指针p,然后再_itemPool++的操作,然后再将p返回给用户即可。
-
释放过程(delete)
比如我们现在的内存分配状况如图所示:现在用户需要归还第 1 块内存。
我们首先要归还块的首地址指向_itemPool的下一个块,然后再将_itemPool指向归还的块的地址。即可
使用队列的push和pop入队和出队操作模拟一个简单对象池的实现的例子
#include <iostream>
using namespace std;
/*
运算符的重载 :成员方法、全局方法
内存池 进程池 线程池 连接池 对象池
*/
template<typename T>
class Queue
{
public:
Queue()
{
_front = _rear = new QueueItem();
}
~Queue()
{
QueueItem *cur = _front;
while (cur != nullptr)
{
_front = cur->_next;
delete cur;
cur = _front;
}
}
Queue(const Queue&obj) = delete;
void operator=(const Queue&obj) = delete;
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 _rear == _front; }
struct QueueItem//产生一个QueueItem 的对象池
{
QueueItem(T data = T()) :_data(data),_next(nullptr) {}
void *operator new(size_t size)
{
if (_itemPool == nullptr)
{
//由于我们不知道QueueItem的具体大小(模板未进行实例化)所以我们这里用char 1字节作为new的基准
_itemPool = (QueueItem*)new char[POOL_ITEM_SIZE * sizeof(QueueItem)];
QueueItem *p = _itemPool;
//把开辟的POOL_ITEM_SIZE个块关联起来
for (; p != _itemPool + POOL_ITEM_SIZE - 1; ++p)
{
p->_next = p + 1;
}
//最后一块的next置为空
p->_next = nullptr;
}
//将第一块可以分配的块分配给用户
QueueItem *p = _itemPool;
_itemPool = _itemPool->_next;
return p;
}
void operator delete(void *ptr)
{
//进行归还操作
QueueItem *p = (QueueItem*)ptr;
p->_next = _itemPool;
_itemPool = p;
}
static QueueItem *_itemPool;
static const int POOL_ITEM_SIZE = 1000000;
T _data;
QueueItem *_next;
};
private:
QueueItem *_front;//指向头节点
QueueItem *_rear;//指向队尾
};
//typename 告诉编译器后面是类型
template<typename T>
typename Queue<T>::QueueItem *Queue<T>::QueueItem::_itemPool = nullptr;
int main()
{
Queue<int> que;
for (int i = 0; i < 1000000; ++i)//大量的new delete;不划算
{
que.push(i);
que.pop();
}
cout << que.empty() << endl;
return 0;
}