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];
}
}
输出结果:
48
0000022921F9FE30 0
0000022921F9FE60 1
0000022921F9FE90 2
0000022921F9FEC0 3
0000022921F9FEF0 4
0000022921FA1C80 5
0000022921FA1CB0 6
0000022921FA1CE0 7
0000022921FA1D10 8
0000022921FA1D40 9
0000022921FA1DB0 10
可以看到,因为我们将CHUNK设为5,每5个内存相邻。
总结:
如果我们看STL源码,可以发现,容器在调用分配器(内存池)时,基本都是这种调用方法。其实现原理会比我们简单的链表复杂一些,但是万变不离其宗,内存池是原理总是先申请一片内存,然后集中的去管理它,对内存的申请和释放内存进行实现。