内存管理(三)内存池

前言

我们每一次使用new来给类对象分配内存的时候都需要在所分配空间的头尾加上两个cookie来保存所分配内存的大小,这就增加了八个字节的开销。所以想要减小这个开销的办法就是一次性开辟一个大的内存,将这个大的内存划分成固定大小的块,块与块之间用指针连接(形成了一个链表),每次new的时候从这个内存里返回一块空间,delete的时候再将这块内存放回大的内存中去。这样的方式就形成了一个内存池。根据之前文章提到的内容,我们可以通过重载operator new 和 operator delete 的方式来实现内存池。

正文

1.成员指针实现内存池

class A
{
private:
 A * next;  //指针
 static A* freeStore;  //静态指针存放内存池的首地址
 static const int Chunk; //每次申请的块数
 public:
 int i;
 A(int i):i(i){}
 void* operator new(size_t size) 
 {
  A* p;
  if(!freeStore) //内存池中没有剩余空间,重新申请
  {
    size_t chunk = Chunk*size;
    freeStore = p = (A*)(new char[chunk]);
    for(;p!=&freeStore[Chunk-1];p++)
    {
      p->next = p+1;
    }
    p->next = NULL;
  }
  p = freeStore;
  freeStore = freeStore->next;
  return p;
 }
 void operator delete(void *p,size_t size)
 {
   (A*)p->next = freeStore;
   freeStroe = (A*)p;
 }

};
A* A::freeStore = NULL;
const int Chunk = 24

这种方式实现的内存池,会在类的内部放一个成员指针,这造成了每个该类对象都需要给这个指针分配空间,然而一旦内存被分配结束,指针就是去了作用,白白浪费掉了四个字节。所以我们采用嵌入式指针的方式来实现块与块之间的连接。

2.嵌入式指针实现内存池

class A
{
private:
  struct obj
  {
    A* next;
  };
  static A* freeStore;  //静态指针存放内存池的首地址
  static const int Chunk; //每次申请的块数
public:
 int i;
 A(int i):i(i){}
 void* operator new(size_t size)
 {
  A* p;
  if(!freeStore)
  {
     size_t chunk = Chunk * size;
     freeStore = p = (A*)(new char[chunk]);
     for(;p!=freeStore[Chunk-1];p++)
     {
           ((obj*)p)->next = p+1;
     }
      ((obj*)p)->next = NULL;
  }
  p = freeStore;
  freeStore = ((obj*)freeStore)->next;
  return p;
 }
 void operator delete(void *p,size_t size)
 {
     (obj*)p->next = freeStore;
     freeStore = (A*)p;
 }
};
A* A::freeStore = NULL;
const int Chunk = 24

我们将每个内存块的前四个字节看成一个指针,在内存池中的块,用这个指针来连接下一块内存;一旦被拿出了内存池,前四个字节就作为类成员的地址空间正常使用。这样的嵌入式指针既能实现作用,又不额外占用空间。但是使用这种方式的前提就是类的大小必须大于等于四字节。

3.静态的allocator

class allocator
{
private:
  struct obj
  {
   obj* next;
  };
  obj * freeStore = NULL;
  const int Chunk = 24;
public:
  void * allocate(size_t size)
  {
     obj* p;
     if(!freeStore)
     {
       size_t chunk = Chunk *size;
       freeStore  = p = (obj*)(new char[chunk]);
       for(int i=0;i<Chunk-1;i++)
       {
           p->next = (obj*)((char*)p+size);
       }
       p->next = NULL;
     }
     p = freeStore;
     freeStore = freeStore->next;
     return p;
  }
  void deallocate(void *p,size_t size)
  {
       ((obj*)p)->next = freeStore;
       freeStore = (obj*)p;
  }
};
class A
{
private:
 static allocator myAlloc;
publicint i;
 A(int i):i(i){}
 void* operator new(size_t size)
 {
  return myAlloc.allocate(size);
 }
 void* operator delete(void *p,size_t size)
 {
    myAlloc.deallocate(p,size);
 }
};
allocator A::myAlloc;

为了方便在不同类中实现内存池,我们封装了一个allocator类用于内存分配。在自定义类的内部设置allocator的静态变量,通过重载operator new 和 operator delete 来调用静态allocator变量中的allocate 和 deallocate接口实现上述功能。

为了更方便使用,可以使用宏定义来实现静态变量定义和接口调用。

//静态变量定义和接口调用的宏
#define DECLEAR_POOL_ALLOC()\
public:\
  void* operator new(size_t size){return myAlloc.allocate(size);}\
  void operator delete(void*p,size_t size){myAlloc.deallcoate(p,size);}\
protected:\
  static allocator myAlloc;
//静态变量初始化的宏  
#define IMPLEMENT_POOL_ALLOC(class_name)\
allocator class_name::myAlloc;

class A
{
public:
 int i;
 A(int i) :i(i){}
 DECLEAR_POOL_ALLOC()
};
IMPLEMENT_POOL_ALLOC(A)

这样就完成了一个类内部内存分配器的设计。

猜你喜欢

转载自blog.csdn.net/weixin_45074185/article/details/107203921