内存池是用来改善new/delete内存管理机制可能造成的运行效率低下问题的一种技术
经典的内存池技术,是用一种用于分配大量大小相同的小对象技术,加快内存分配或者释放的过程。
在看书的过程中,我对简单的实现一个内存池的实现理解是:
1、让我们在堆上面申请到的内存是连续的。
2、我们放在内存池中的某个对象,当我们delete它后,我们要再次申请内存的时候,就在刚刚delete的那个位置重新申请,而不是在内存的某个位置申请。
3、使用多大内存就申请多大的内存存放在内存池。
根据以上条件来实现,则可以通过一个数组链表来实现,达到上面的第一个条件;第二个条件,我们通过内存块指针与内存节点指针的操作来实现;第三个条件,通过模板来实现。
(代码与书上的代码相差不大,差别就在于链表指向的顺序,与添加了结构体的构造函数)
代码:
//my_mempool.h
//模板参数为对象的size,与数目
template<int Objectsize,int Objectnumsize=20>
class mempool {
private:
//Freenode是自由节点,指未被使用的内存节点
struct Freenode {
Freenode *next;
char mes[Objectsize];
Freenode() :next(nullptr) {}
};
//Mmemnode是内存块节点,默认存放20个对象
struct Memnode {
Memnode *next;
Freenode pfree[Objectnumsize];
Memnode() :next(nullptr) {}
};
Memnode *Memheader; //指向内存块第一个节点
Freenode *Freeheadr;//指向自由内存的头节点
const int Memblocksize;//内存块大小
const int Nodesize;//申请的节点大小
public:
//每个节点除了我们对象本身的大小,还要加上每个节点中的指针大小
mempool() :Nodesize(Objectsize + sizeof(Freenode*)), Memblocksize(sizeof(Memnode*) + Objectnumsize*(Objectsize + sizeof(Freenode*)))
{
Memheader = nullptr;
Freeheadr = nullptr;
}
~mempool()
{
Memnode *ptr;
while (Memheader)
{
ptr = Memheader->next;
delete Memheader;
Memheader = ptr;
}
}
//测试每个节点大小是否与初始化Nodesize相同
void printObjectSize()
{
cout << sizeof(Memheader->pfree)/Objectnumsize << endl;
}
void* malloc();
void free(void* ptr);
};
malloc申请内存的过程是,判freehead也就是内存块中是否还有自由节点,若没有则申请一个内存块如下代码中的memblock,然后初始化memblock的自由节点链表与freeheadr,并把memblock加入内存块链表管理。然后返回指向Freeheadr的指针,也就意味着我们占用了这块内存节点,然后freeheadr指向下一个节点。
template<int Objectsize, int Objectnumsize>
void * mempool<Objectsize, Objectnumsize>::malloc()
{
if (Freeheadr == nullptr)
{
Memnode *memblock = new Memnode;
for (int i = 1; i < Objectnumsize; i++)
memblock->pfree[i-1].next = &memblock->pfree[i];
Freeheadr = &memblock->pfree[0];
memblock->next = Memheader;
Memheader = memblock;
}
void *temp = Freeheadr;
Freeheadr = Freeheadr->next;
return temp;
}
free的过程就是把原来占用节点重新加入放回自由节点中,将要free的指针设置为自由节点头指针,而原先的freeheadr则为该free的指针的next,在下一次malloc的时候直接返回该指针则达到了申请的内存地址为上一次销毁的内存的地址,例如,p1->p2->freeheadr,我们free(p1)后,就成了p2->原来的freeheadr,p1->原来的freeheadr,而当前的freeheadr=p1,在下次malloc的时候,就返回p1的地址,然后malloc的时候freeheadr=freeheadr->next,也就是p1与p2指向的地方即原先的freeheadr处。
template<int Objectsize, int Objectnumsize>
void mempool<Objectsize, Objectnumsize>::free(void *ptr)
{
Freenode *currfreeheadr = (Freenode*)ptr;
currfreeheadr->next=Freeheadr;
Freeheadr = currfreeheadr;
}
———————————————–main.cpp————————————————–
//main.cpp
class B {
private:
static int m_count;
int num[5];
public:
B() {
m_count++;
for (int i = 0; i < 5; i++)
num[i] = m_count + i;
}
void show()
{
cout << this << " : ";
for (int i = 0; i < 5; i++)
cout << num[i] << " ";
cout << endl;
}
void* operator new(size_t size);
void operator delete(void* p);
};
//重载new
void * B::operator new(size_t size)
{
return thepool::mp.malloc();
}
//重载delete
void B::operator delete(void* p)
{
return thepool::mp.free(p);
}
class thepool {
public:
static mempool<sizeof(B), 2>mp; //存储两个B类型对象
friend class B;
};
int B::m_count = 0;
mempool<sizeof(B), 2> thepool::mp;
int main()
{
cout << "sizeof(B) : " << sizeof(B) << endl;
B *p1 = new B;
p1->show();
B *p2 = new B;
p2->show();
cout << "(int)p2 - (int)p1 : " <<(int)p2 - (int)p1 << endl;
delete p1;
p1 = NULL;
p1 = new B;
p1->show();
B *p3 = new B;
cout << "(int)p3 - (int)p2 : " << (int)p3 - (int)p2 << endl;
p3->show();
B *p4 = new B;
p4->show();
cout << "(int)p2 - (int)p1 : " << (int)p4 - (int)p3 << endl;
delete p4;
delete p3;
delete p2;
delete p1;
return 0;
}
结果:
从结果我们可以看到,B的类型大小为20,加上指针则为24,我们申请的内存池是一个内存块存储两个对象,所以p1与p2相差的24,由于只存放两个,所以申请p3的时候又申请了一个内存块,所以p3与p2之间的距离差很大。
-----------------------------------------------在写代码的时候碰上的问题-----------------------------------------------
由于C++基础不扎实,不清楚可以使用模板参数作为数组大小(动态解析机制),我看到用模板里面的参数作为数组的大小的时候,我感觉编译不能通过,一般数组大小都是使用常量表达式,而C++模板可以推导出值,相当于参数的值可以推导出来,相当于这些值是常量表达式。。不知道是不是这么理解。。
然后因为用模板参数作为数组大小,在写的时候,下面代码在VS2015中,memblock显示没有调用的成员,但是按逻辑写下去是可以运行的,可能这与动态解析有关。
template<int Objectsize, int Objectnumsize>
void * mempool<Objectsize, Objectnumsize>::malloc()
{
if (Freeheadr == nullptr)
{
Memnode *memblock = new Memnode;
for (int i = 1; i < Objectnumsize; i++)
memblock->pfree[i-1].next = &memblock->pfree[i];
Freeheadr = &memblock->pfree[0];
memblock->next = Memheader;
Memheader = memblock;
}
void *temp = Freeheadr;
Freeheadr = Freeheadr->next;
return temp;
}