技术宅的结界

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

QQ登录

只需一步,快速开始

搜索
热搜: 下载 VB C 实现 编写
查看: 340|回复: 0
收起左侧

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

[复制链接]

7

主题

11

帖子

5145

积分

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

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

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

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

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

        首先定义一个空引用,它未指向任何值:
[C++] 纯文本查看 复制代码
HStudent stu;

        调用Initalize()实例化对象,这里实际分配内存空间:
[C++] 纯文本查看 复制代码
stu.Initalize();

        当stu指向的堆内存没有任何H句柄对象指向时就会被内存池标记为可用,从而给予其它对象使用。Student的成员函数通过->操作符访问。
具体实现:  
       首先实现一个简单的动态数组模板类Vector:
        
[C++] 纯文本查看 复制代码
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[1]),
                        m_size(1){}
                Vector(unsigned size) :
                        m_vector(new T[size]),
                        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[m_size];
                        memcpy_s(temp, m_size, m_vector, m_size);
                        delete[] m_vector;
                        m_vector = temp;
                }        
        };

        接着实现内存池模板类,包含一个含vector节点的链表。当程序需要更多内存时,就会创建新的Vector节点。每个节点依次是2^(n-1)个元素。如果用户需要一个内存,就通过查询内存所在变量的引用计数是否为0,是就说明没人使用,就返回此地址。
[C++] 纯文本查看 复制代码
 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;
                        }
                }
                
        };

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

[C++] 纯文本查看 复制代码
 
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类。
[C++] 纯文本查看 复制代码
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开头的句柄类。
[C++] 纯文本查看 复制代码
#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的速度差距:
[C++] 纯文本查看 复制代码
 DWORD t1 = GetTickCount();
                unsigned i = REPLAY_COUNT;
                while (i--)
                {
                        //STU_NUMBER个
                        HStudent stu3[STU_NUMBER];
                        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[STU_NUMBER];
                        delete stu4;
                }
                t2 = GetTickCount();
                std::cout << "花费时间:" << t2 - t1 << "ms" << std::endl;

        得出的数据如下:

1.png



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








本版积分规则

QQ|申请友链|Archiver|手机版|小黑屋|技术宅的结界 ( 滇ICP备16008837号|网站地图

GMT+8, 2018-10-19 10:38 , Processed in 0.099055 second(s), 37 queries , Gzip On.

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

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