找回密码
 立即注册→加入我们

QQ登录

只需一步,快速开始

搜索
热搜: 下载 VB C 实现 编写
查看: 1864|回复: 0

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

[复制链接]

7

主题

4

回帖

5154

积分

用户组: 技术宅的结界VIP成员

UID
641
精华
1
威望
13 点
宅币
5111 个
贡献
1 次
宅之契约
0 份
在线时间
11 小时
注册时间
2015-1-28
发表于 2017-7-31 21:25:26 | 显示全部楼层 |阅读模式

欢迎访问技术宅的结界,请注册或者登录吧。

您需要 登录 才可以下载或查看,没有账号?立即注册→加入我们

×
C++简单实现GC和内存池本章前言:
        这章就是为了避开传统的new\delete创建堆对象的手法,如果使用不当指针容易出现问题,所以本章简单的实现了引用计数垃圾回收,使用起来貌似还不错。Bug还有待测试。
最终效果:
        通过DND_GC_CLASS宏来定义一个Student类后,用户还是能通过正常的方法使用Student类。但宏额外生成了HStudent类,通过这个类就能方便的实现自动垃圾回收(类似C#中引用变量的效果)。
  1. #include "DNDGC.h"
  2. DND_GC_CLASS(Student)
  3. public:
  4.         int id;
  5.         char data[40000];
  6.         Student():
  7.         id(0){}
  8.         void Init(int idi)
  9.         {
  10.                 id = idi;
  11.         }
  12.         bool operator<(const Student& b)
  13.         {
  14.                 return id < b.id;
  15.         }
  16. };
复制代码

        首先定义一个空引用,它未指向任何值:
  1. HStudent stu;
复制代码

        调用Initalize()实例化对象,这里实际分配内存空间:
  1. stu.Initalize();
复制代码

        当stu指向的堆内存没有任何H句柄对象指向时就会被内存池标记为可用,从而给予其它对象使用。Student的成员函数通过->操作符访问。
具体实现:  
       首先实现一个简单的动态数组模板类Vector:
      
  1. template<typename T>
  2.         class Vector
  3.         {
  4.         public:
  5.                 T& operator[](unsigned i)
  6.                 {
  7.                         assert(i < m_size);
  8.                         return m_vector;
  9.                 }
  10.                 T& At(unsigned i)
  11.                 {
  12.                         while(i >= m_size)
  13.                                 _extend();
  14.                         return m_vector;
  15.                 }
  16.                 Vector() :
  17.                         m_vector(new T[1]),
  18.                         m_size(1){}
  19.                 Vector(unsigned size) :
  20.                         m_vector(new T[size]),
  21.                         m_size(size){}
  22.                 ~Vector()
  23.                 {
  24.                         delete[] m_vector;
  25.                 }
  26.                 unsigned GetSize(){return m_size;}
  27.         private:
  28.                 T* m_vector;
  29.                 unsigned m_size;
  30.                 void _extend()
  31.                 {
  32.                         m_size <<= 1;
  33.                         T* temp = new T[m_size];
  34.                         memcpy_s(temp, m_size, m_vector, m_size);
  35.                         delete[] m_vector;
  36.                         m_vector = temp;
  37.                 }        
  38.         };
复制代码

        接着实现内存池模板类,包含一个含vector节点的链表。当程序需要更多内存时,就会创建新的Vector节点。每个节点依次是2^(n-1)个元素。如果用户需要一个内存,就通过查询内存所在变量的引用计数是否为0,是就说明没人使用,就返回此地址。
  1. template<class T>
  2.         class PoolArray
  3.         {
  4.         private:
  5.                 //页面组(一个页面就是一个数组而已)
  6.                 std::list<Vector<T>*> m_pages;
  7.         public:
  8.                 //构造时初始化第一个页,大小为1
  9.                 PoolArray()
  10.                 {
  11.                         //注意类型是指针
  12.                         Vector<T>* a = new Vector<T>(1);
  13.                         std::cout << "使用内存:" << (sizeof(T)) / 1024.0f << "KB" << std::endl;
  14.                         m_pages.push_back(a);
  15.                 }
  16.                 //要取一个位置
  17.                 T* GetSite()
  18.                 {
  19.                 loop:
  20.                         unsigned num = 0;
  21.                         for (auto iter = m_pages.begin(); iter != m_pages.end(); ++iter)
  22.                         {
  23.                                 Vector<T>* a = *iter;
  24.                                 for (unsigned i = 0; i < a->GetSize(); ++i)
  25.                                 {
  26.                                         GCObject* obj = (GCObject*)&((*a));
  27.                                         if (obj->m_refNum == 0)
  28.                                         {
  29.                                                 obj->m_refNum++;
  30.                                                 return (T*)obj;
  31.                                         }
  32.                                 }
  33.                                 num += a->GetSize();
  34.                         }
  35.                         //没有空位,则添加新页,为以前总和的两倍
  36.                         Vector<T>* next = new Vector<T>(num << 1);
  37.                         std::cout << "使用内存:" << ((num << 1) * sizeof(T)) / 1024.0f << "KB"  << std::endl;
  38.                         m_pages.push_back(next);
  39.                         goto loop;
  40.                 }
  41.                 //析构,结束程序的时候调用
  42.                 ~PoolArray()
  43.                 {
  44.                         for (auto iter = m_pages.begin(); iter != m_pages.end(); ++iter)
  45.                         {
  46.                                 Vector<T>* a = *iter;
  47.                                 std::cout << "释放内存:" << (a->GetSize() * sizeof(T)) / 1024.0f << "KB" << std::endl;
  48.                                 delete a;
  49.                         }
  50.                 }
  51.                
  52.         };
复制代码

        其中使用内存和释放内存的地方写有调试语句,效果如下:
          2.png
        接着写个接口让程序来调用,其中static PoolArray<T> a用来存储内存池用到的内存:


  1. class Mempool{
  2.                 //用户禁止使用
  3.         private:
  4.                 Mempool() {};
  5.                 //查找T类型的auto_array
  6.                 template<class T>
  7.                 static PoolArray<T>& FindPoolArray()
  8.                 {
  9.                         static PoolArray<T> a;
  10.                         return a;
  11.                 }
  12.         public:
  13.                 //返回指定类型的内存池地址
  14.                 template<class T>
  15.                 static T* GetSite()
  16.                 {
  17.                         return FindPoolArray<T>().GetSite();
  18.                 }
  19.         };
复制代码

        如果代码中想要分配指定类型的一块内存就应该调用Mempool::GetSite<TypeName>()。但规定是T得继承于GCObject类。
  1. class GCObject
  2. {
  3.         //friend class DND::Mempool;
  4.         template<class T>
  5.         friend class DND:oolArray;
  6. public:
  7.         unsigned GetRefNum()
  8.         {
  9.                 return m_refNum;
  10.         }
  11.         void PrintTypeName()
  12.         {
  13.                 std::cout << typeid(this).name() << std::endl;
  14.         }
  15.         unsigned m_refNum;
  16.         GCObject() : m_refNum(0){}
  17.         virtual bool operator<(const GCObject& b)
  18.         {
  19.                 return true;
  20.         }
  21. };
复制代码

        GCObject类主要包含引用计数变量,使用此框架的类都应该继承它。DND_GC_CLASS宏就让参数继承它,并为其生成一个H开头的句柄类。
  1. #define DND_GC_CLASS(ClassName)\
  2. class ClassName;\
  3. class H##ClassName\
  4. {\
  5. public:\
  6.         ClassName* operator->(){assert(m_p && "This is null reference!"); return (ClassName*)m_p;}\
  7.         ~H##ClassName(){if(m_p && !--m_p->m_refNum){m_p = 0;}}\
  8.         H##ClassName() : m_p(0){}\
  9.         H##ClassName(const H##ClassName& b){m_p = (GCObject*)b.m_p;++m_p->m_refNum;}\
  10.         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;}\
  11.         bool operator<(const H##ClassName& b){return *m_p < *b.m_p;}\
  12.         void Initalize(){if(m_p && !--m_p->m_refNum){m_p = 0;} m_p = (GCObject*)DND::Mempool::GetSite<ClassName>();}\
  13. private:\
  14.         GCObject* m_p;\
  15.         static void* operator new(size_t size){};\
  16.         static void operator delete(void* ptr){};\
  17. };\
  18. class ClassName : private GCObject\
  19. {\
  20.         friend class H##ClassName;\
复制代码

        最后我做了一下测试,对比了和直接new/delete的速度差距:
  1. DWORD t1 = GetTickCount();
  2.                 unsigned i = REPLAY_COUNT;
  3.                 while (i--)
  4.                 {
  5.                         //STU_NUMBER个
  6.                         HStudent stu3[STU_NUMBER];
  7.                         for (unsigned i = 0; i != STU_NUMBER; ++i)
  8.                         {
  9.                                 stu3.Initalize();
  10.                                 //stu3->rint();
  11.                         }
  12.                 }
  13.                 DWORD t2 = GetTickCount();
  14.                 std::cout << "花费时间:" << t2 - t1  << "ms"<< std::endl;
  15.                 //////////////////////////////////////////////////////////
  16.                 t1 = GetTickCount();
  17.                 i = REPLAY_COUNT;
  18.                 while (i--)
  19.                 {
  20.                         Student* stu4 = new Student[STU_NUMBER];
  21.                         delete stu4;
  22.                 }
  23.                 t2 = GetTickCount();
  24.                 std::cout << "花费时间:" << t2 - t1 << "ms" << std::endl;
复制代码

        得出的数据如下:

1.png



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








回复

使用道具 举报

QQ|Archiver|小黑屋|技术宅的结界 ( 滇ICP备16008837号 )|网站地图

GMT+8, 2024-4-20 17:23 , Processed in 0.034325 second(s), 30 queries , Gzip On.

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表