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]