C++内存管理-内存池3
因为前面的内存池是针对每一个类,所以我们必须对每一个类重写一遍几乎相同的operator new、operator delete。这在工程代码中是不可以被接受,在项目中最重要的就是不要重复的造轮子。
下面将展示一种写法,将内存池的实现,写为一个operator类,所有想要实现内存池的类只要静态包含,并调用operator类的申请、释放函数即可,这样就使得代码可以重复的被使用。
//内存池分配器class allocator{private://和调用的函数共用同一块内存,放入next指针struct obj {struct obj* next;};public://申请内存接口void* allocate(size_t);//释放内存接口void deallocate(void*, size_t);private://内存池头指针obj* freeStore = nullptr;//每次申请内存个数,可以随意设置const int CHUNK = 5;};void* allocator::allocate(size_t size){obj* p;//如果内存池为空,则申请CHUNK个内存if (!freeStore){size_t chunk = CHUNK * size;freeStore = p = (obj*)malloc(chunk);//将内存池串成一串for (int i = 0; i < (CHUNK - 1); ++i) {p->next = (obj*)((char*)p + size);p = p->next;}p->next = nullptr;}p = freeStore;freeStore = freeStore->next;return p;}void allocator::deallocate(void* p, size_t){//将释放的内存放回内存池中((obj*)p)->next = freeStore;freeStore = (obj*)p;}
allocator里面的实现和内存池2里基本一样,只是将其包装成一个独立的类。
所以当我们某一个类(Foo)用到内存池时,就可以这么使用:
class Foo{public:long L;string str;//类的内存池static allocator myAlloc;public:Foo(long l):L(l){}//调用内存池获取内存static void* operator new(size_t size){return myAlloc.allocate(size);}//调用内存池释放内存static void operator delete(void* pdead, size_t size) {return myAlloc.deallocate(pdead, size);}};allocator Foo::myAlloc;void pool_3_test(){Foo* p[100];cout << sizeof(Foo) << endl;//申请100个内存for (int i = 0; i < 100; ++i) {p[i] = new Foo(i);}//查看前11个内存地址for (int i = 0; i < 11; ++i) {cout << p[i] << " " << p[i]->L << endl;}//delete 申请的内存for (int i = 0; i < 100; ++i) {delete p[i];}}
输出结果:
480000022921F9FE30 00000022921F9FE60 10000022921F9FE90 20000022921F9FEC0 30000022921F9FEF0 40000022921FA1C80 50000022921FA1CB0 60000022921FA1CE0 70000022921FA1D10 80000022921FA1D40 90000022921FA1DB0 10
可以看到,因为我们将CHUNK设为5,每5个内存相邻。
总结:
如果我们看STL源码,可以发现,容器在调用分配器(内存池)时,基本都是这种调用方法。其实现原理会比我们简单的链表复杂一些,但是万变不离其宗,内存池是原理总是先申请一片内存,然后集中的去管理它,对内存的申请和释放内存进行实现。
