简单内存池
内存池实际上是一种内存的分配模式。它不同于我们平常用的new,malloc来让系统进行内存的分配,它是我们人为的从系统中申请出来一块内存来进行分配。如果我们让系统自动分配则会产生许多内存碎片,这会降低我们的行能,造成内存浪费。
内存碎片又分为外内存碎片和内内存碎片,
其中外存碎片是指:已经分配出去的内存释放后,该内存不能再重新分配。
内碎片是指:开辟过程中,使用点需要的空间小于实际分配的空间所造成的空间浪费。
如下图,我们再使用点我们实际需要的大小是4+1,5个字节,但是系统要字节对齐,所以就会分配出来8个字节的大小供我们使用,那么剩余的三个字节就是内存浪费。
那我们使用内存池有什么有点呢?首先我们不用频繁的new,malloc来申请空间,使得效率提高。其次我们申请的内存池一般都是一块很大的内存,当我们释放该内存时是及不容易产生内存碎片的,内存资源利用率也会提高。
#include <iostream>
template <typename T>
class queue;
const int max_size = 10;
template <typename T>
class queueitem//队列内存池
{
public:
queueitem(T val = T()) //构造函数。初始化val,使它可以被T类型的任意值赋值
:mdata(val), pnext(NULL)
{}
void* operator new(size_t size)//一种用来记录大小的数据类型(相当于定义了一个无符号整形的size)
{
if (pool == NULL)//如果pool指向空
{
pool = (queueitem<T>*)new char[size * max_size];//申请size*10个大小的char 类型空间,并把它强转为queueitem<T>*类型,
//此时pool指向了新开辟空间的第一个空间
queueitem<T>* pcur = pool;//定义一个指向pool的指针
for (pcur; pcur < pool + max_size - 1; pcur = pcur + 1)//pcyr自加往下走
/*pool+max_size-1,pool指向了最后一个空间,pcur小于它,代表pcur指向了倒数第二个*/
{
pcur->pnext = pcur + 1;//从首地址开始,它的pnext域指向了下一个位置,使得空间成为一个链表
}
pcur->pnext = NULL;//链表完成后将第一个地址的pnext位置置为空
}
queueitem<T>* rt = pool->pnext;
pool->pnext = rt->pnext;
return rt;
//void* ptr = pool;//新申请的空间供外部使用时,需要将第一个节点赋给外界
//pool = pool->pnext;//将第一个节点往后偏移一下
//return ptr;//将ptr返回出去
}
void operator delete(void* ptr)//删除节点,将节点回收到内存池中
{
queueitem<T>* qptr = (queueitem<T>*)ptr;//将要回收的节点赋给qptr void*没办法直接处理,所以将其转换为queueitem<T>*的类型
qptr->pnext = pool;//将要回收的指针重新指向pool
pool = qptr;//将qptr作为首地址,调整pool位置
}
private:
template <typename T>
friend class queue;
T mdata;
queueitem<T>* pnext;
static queueitem<T>* pool;//定义一个静态的变量pool
};
template <typename T>
queueitem<T>* queueitem<T>::pool = NULL;//对pool的声明
template <typename T>
class queue
{
public:
queue()
{
phead = ptail = new queueitem<T>();//头指针和尾指针都指向同一个节点
}
~queue()
{
queueitem<T>* ptt = phead;
queueitem<T>* pnext;
while (ptt != NULL)//如果头指针不为空
{
pnext = ptt->pnext;//把后续保留下来
delete ptt;
//调用析构函数
//调用operator delete释放
ptt = pnext;
}
phead = NULL;
}
void push(T val)//入队操作
{
queueitem<T>* pnewitem = new queueitem<T>(val);//新申请一个节点从内存池中
ptail->pnext = pnewitem;//尾指针的pnext指向了新申的节点
ptail = ptail->pnext;//尾指针往后走
}
bool empty()//判空
{
return (phead == ptail) && (phead != NULL);
}
void pop()//出队
{
if (empty())
{
throw std::exception("queu is empty");
}
queueitem<T>* ptr = phead->pnext;//新申请的节点指向了头部指向的下一个位置,第一个数据节点
phead->pnext = ptr->pnext;//头指针的下一个位置指向了第一个指针的pnext域。相当于头指针和第一个节点同时指向了第二个节点
delete ptr;//将第一个节点释放
}
T back()
{
if (empty())
{
throw std::exception("queu is empty");
}
return ptail->mdata;
}
T front()
{
if (empty())
{
throw std::exception("queu is empty");
}
return phead->pnext->mdata;
}
private:
queueitem<T>* phead;
queueitem<T>* ptail;
};
int main()
{
queue<int> que;
for (int i = 0; i < 5; i++)
{
que.push(i + 1);
}
que.pop();
auto qfront = que.front();
auto qback = que.back();
std::cout << "queue.front:" << qfront << std::endl;
std::cout << "queue.fback:" << qback << std::endl;
return 0;
}
其中size_t实际上是unisgned int 类型的别名,即无符号整数
根据C标准,rsize_t类型必须是 size_t
通用内存池
虽然我们再上面已经开辟好了一个内存池,但是它的局限性太明显。即它只能再queuepool中申请内存。所以我们就会想开辟一个通用的内存池,不管哪个类来申请时我们都可以分配出去内存供外部使用。
alloc:开辟内存
dealloc:回收内存
我们只需要在申请类中的operator new中调动alloc接口开辟内存
#include<iostream>
#include <string>
const int max_size = 10;
template <typename T>//我们不知道将存放什么类型的数据,所以给出T模板
class mem_pool
{
public:
mem_pool()
{
pool = NULL;//将poll指向NULL
}
void* Alloc(rsize_t size)//计算的是student数据域的大小
{
if (pool == NULL)
{
pool = (node*)new char[(size+4)* max_size];//在内存池中,不但有指针域还有数据域
node* paa = pool;
for (paa; paa < pool + max_size - 1; paa += 1)
{
paa->pnext = paa + 1;
}
paa ->pnext= NULL;//链表完成后最后一个节点的pnext位置置为空
}
void* pcc = pool;
pool = pool->pnext;
return pcc;
}
void Dealloc(void* ptr)
{
node* pee = (node*)ptr;//将ptr从(void*)转换为(node*)
if (pee == NULL)
{
return;
}
pee->pnext = pool;
pool = pee;
}
private:
friend class student;
class node
{
public:
node(T val)//针对节点的构造函数
:mdata(val),pnext(NULL)
{}
public:
T mdata;
node* pnext;//将数据域和指针域放在一个节点中
};
node* pool;//将节点拿出来生成一个静态链表的结构
};
class student
{
public:
student(std::string name, std::string id, std::string age)
:mname(name),mid(id),mage(age)
{}
void* operator new(size_t size)
{
return mm.Alloc(size);//内存池对象通过alloc接口开辟内存
}
void operator delete(void* ptr)
{
mm.Dealloc(ptr);//内存池对象通过dealloc来回收内存
}
private:
std::string mname;
std::string mid;
std::string mage;
static mem_pool<student> mm;//因为是所有的学生共享同一个内存池,所以我们把它设为全局静态变量
};
mem_pool<student> student::mm;//对静态全局变量的申明
int main()
{
student* pstu1 = new student("zhangsan", "001", "20");
student* pstu2 = new student("lisi", "002", "21");
std::cout <<"laile:" << pstu1 << std::endl;
std::cout << "laile:" << pstu2 << std::endl;
return 0;
}