略游 发表于 2017-7-31 21:25:26

C++简单实现CG和内存池

C++简单实现GC和内存池本章前言:
      这章就是为了避开传统的new\delete创建堆对象的手法,如果使用不当指针容易出现问题,所以本章简单的实现了引用计数垃圾回收,使用起来貌似还不错。Bug还有待测试。
最终效果:
      通过DND_GC_CLASS宏来定义一个Student类后,用户还是能通过正常的方法使用Student类。但宏额外生成了HStudent类,通过这个类就能方便的实现自动垃圾回收(类似C#中引用变量的效果)。
#include "DNDGC.h"
DND_GC_CLASS(Student)
public:
      int id;
      char data;
      Student():
      id(0){}
      void Init(int idi)
      {
                id = idi;
      }
      bool operator<(const Student& b)
      {
                return id < b.id;
      }
};
      首先定义一个空引用,它未指向任何值:
HStudent stu;
      调用Initalize()实例化对象,这里实际分配内存空间:
stu.Initalize();
      当stu指向的堆内存没有任何H句柄对象指向时就会被内存池标记为可用,从而给予其它对象使用。Student的成员函数通过->操作符访问。
具体实现:
       首先实现一个简单的动态数组模板类Vector:
      template<typename T>
      class Vector
      {
      public:
                T& operator[](unsigned i)
                {
                        assert(i < m_size);
                        return m_vector;
                }
                T& At(unsigned i)
                {
                        while(i >= m_size)
                              _extend();
                        return m_vector;
                }
                Vector() :
                        m_vector(new T),
                        m_size(1){}
                Vector(unsigned size) :
                        m_vector(new T),
                        m_size(size){}
                ~Vector()
                {
                        delete[] m_vector;
                }
                unsigned GetSize(){return m_size;}
      private:
                T* m_vector;
                unsigned m_size;
                void _extend()
                {
                        m_size <<= 1;
                        T* temp = new T;
                        memcpy_s(temp, m_size, m_vector, m_size);
                        delete[] m_vector;
                        m_vector = temp;
                }      
      };
      接着实现内存池模板类,包含一个含vector节点的链表。当程序需要更多内存时,就会创建新的Vector节点。每个节点依次是2^(n-1)个元素。如果用户需要一个内存,就通过查询内存所在变量的引用计数是否为0,是就说明没人使用,就返回此地址。
template<class T>
      class PoolArray
      {
      private:
                //页面组(一个页面就是一个数组而已)
                std::list<Vector<T>*> m_pages;
      public:
                //构造时初始化第一个页,大小为1
                PoolArray()
                {
                        //注意类型是指针
                        Vector<T>* a = new Vector<T>(1);
                        std::cout << "使用内存:" << (sizeof(T)) / 1024.0f << "KB" << std::endl;
                        m_pages.push_back(a);
                }
                //要取一个位置
                T* GetSite()
                {
                loop:
                        unsigned num = 0;
                        for (auto iter = m_pages.begin(); iter != m_pages.end(); ++iter)
                        {
                              Vector<T>* a = *iter;
                              for (unsigned i = 0; i < a->GetSize(); ++i)
                              {
                                        GCObject* obj = (GCObject*)&((*a));
                                        if (obj->m_refNum == 0)
                                        {
                                                obj->m_refNum++;
                                                return (T*)obj;
                                        }
                              }
                              num += a->GetSize();
                        }
                        //没有空位,则添加新页,为以前总和的两倍
                        Vector<T>* next = new Vector<T>(num << 1);
                        std::cout << "使用内存:" << ((num << 1) * sizeof(T)) / 1024.0f << "KB"<< std::endl;
                        m_pages.push_back(next);
                        goto loop;
                }
                //析构,结束程序的时候调用
                ~PoolArray()
                {
                        for (auto iter = m_pages.begin(); iter != m_pages.end(); ++iter)
                        {
                              Vector<T>* a = *iter;
                              std::cout << "释放内存:" << (a->GetSize() * sizeof(T)) / 1024.0f << "KB" << std::endl;
                              delete a;
                        }
                }
               
      };
      其中使用内存和释放内存的地方写有调试语句,效果如下:
         
      接着写个接口让程序来调用,其中static PoolArray<T> a用来存储内存池用到的内存:


class Mempool{
                //用户禁止使用
      private:
                Mempool() {};
                //查找T类型的auto_array
                template<class T>
                static PoolArray<T>& FindPoolArray()
                {
                        static PoolArray<T> a;
                        return a;
                }
      public:
                //返回指定类型的内存池地址
                template<class T>
                static T* GetSite()
                {
                        return FindPoolArray<T>().GetSite();
                }
      };
      如果代码中想要分配指定类型的一块内存就应该调用Mempool::GetSite<TypeName>()。但规定是T得继承于GCObject类。
class GCObject
{
      //friend class DND::Mempool;
      template<class T>
      friend class DND:oolArray;
public:
      unsigned GetRefNum()
      {
                return m_refNum;
      }
      void PrintTypeName()
      {
                std::cout << typeid(this).name() << std::endl;
      }
      unsigned m_refNum;
      GCObject() : m_refNum(0){}
      virtual bool operator<(const GCObject& b)
      {
                return true;
      }
};
      GCObject类主要包含引用计数变量,使用此框架的类都应该继承它。DND_GC_CLASS宏就让参数继承它,并为其生成一个H开头的句柄类。
#define DND_GC_CLASS(ClassName)\
class ClassName;\
class H##ClassName\
{\
public:\
      ClassName* operator->(){assert(m_p && "This is null reference!"); return (ClassName*)m_p;}\
      ~H##ClassName(){if(m_p && !--m_p->m_refNum){m_p = 0;}}\
      H##ClassName() : m_p(0){}\
      H##ClassName(const H##ClassName& b){m_p = (GCObject*)b.m_p;++m_p->m_refNum;}\
      H##ClassName& operator=(const H##ClassName& b){if(m_p && !--m_p->m_refNum){m_p = 0;}m_p = (GCObject*)b.m_p;++m_p->m_refNum;return *this;}\
      bool operator<(const H##ClassName& b){return *m_p < *b.m_p;}\
      void Initalize(){if(m_p && !--m_p->m_refNum){m_p = 0;} m_p = (GCObject*)DND::Mempool::GetSite<ClassName>();}\
private:\
      GCObject* m_p;\
      static void* operator new(size_t size){};\
      static void operator delete(void* ptr){};\
};\
class ClassName : private GCObject\
{\
      friend class H##ClassName;\
      最后我做了一下测试,对比了和直接new/delete的速度差距:
DWORD t1 = GetTickCount();
                unsigned i = REPLAY_COUNT;
                while (i--)
                {
                        //STU_NUMBER个
                        HStudent stu3;
                        for (unsigned i = 0; i != STU_NUMBER; ++i)
                        {
                              stu3.Initalize();
                              //stu3->rint();
                        }
                }
                DWORD t2 = GetTickCount();
                std::cout << "花费时间:" << t2 - t1<< "ms"<< std::endl;
                //////////////////////////////////////////////////////////
                t1 = GetTickCount();
                i = REPLAY_COUNT;
                while (i--)
                {
                        Student* stu4 = new Student;
                        delete stu4;
                }
                t2 = GetTickCount();
                std::cout << "花费时间:" << t2 - t1 << "ms" << std::endl;
      得出的数据如下:



作者:略游
日期:17-07-22
QQ:1339484752







页: [1]
查看完整版本: C++简单实现CG和内存池