最近在实现一个静态链表,即链表的节点占据的空间并非等到插入时再在堆空间中动态分配,而是在一块我们预先分配好的内存空间(可以在栈也可以在堆上)中找出一块空闲的内存空间来使用,当要销毁给节点时,不是delete掉该空间而是将该空间置为空闲标志。这就是池的思想,内存池、线程池的核心思想也是如此。池的设计考虑到许多内存管理技巧,本文只是记录自己的一些简单的笔记。
定义模板类MemPoolManage,用于管理泛型数据的所存放的内存地址:
template<typename T, int N>
class MemPoolManage
{
protected:
unsigned char *m_mem; //指向预分配的内存空间
bool m_used[N]; //标志对应的内存空间是否被使用
public:
//返回用于存放变量的内存空间地址
T* CreateMem()
{
int i = 0;
T* ret = NULL;
while (i < N)
{
if (!m_used[i])
{
ret = reinterpret_cast<T* >(m_mem) + i;
cout << "CreateMem: i = " << i << endl;
m_used[i] = true;
break;
}
++i;
}
return ret;
}
//归还存放变量的内存空间
void DestoryMem(T* p)
{
for (int i = 0; i < N; ++i)
{
if (p == reinterpret_cast<T* >(m_mem) + i)
{
m_used[i] = 0;
cout << "DestoryMem: i = " << i << endl;
break;
}
}
}
MemPoolManage()
{
int i = 0;
m_mem = new unsigned char[N];
while (i < N)
{
m_used[i++] = false;
}
}
~MemPoolManage()
{
delete[] m_mem;
}
};
使用:
int main(void)
{
MemPoolManage<int, sz> mem;
int* p1 = mem.CreateMem();
if (!p1)
{
cout << "Menory full" << endl;
}
else
mem.DestoryMem(p1);
return 0;
}
看似解决了变量变量在我们预先分配好的内存空间存放需求,然后这是一个不完美的解决方法。因为该预先分配好的内存空间可以存放任意类型的数据,也就是可以存放自定义的类类型,然而对于类类型,CreateMem()函数也只是单纯的返回内存空间的地址,并没有调用该类的构造函数,DestoryMem()只是单纯的将使用标志位设置为未使用,也没有调用T类型的析构函数,是不是可以这样修改这两个函数:
CreateMem()函数:
//...
if (!m_used[i])
{
ret = reinterpret_cast<T* >(m_mem) + i;
ret->T(); //调用T类型的构造函数
cout << "CreateMem: i = " << i << endl;
m_used[i] = true;
break;
}
//...
DestoryMem()函数:
//...
if (p == reinterpret_cast<T* >(m_mem) + i)
{
m_used[i] = 0;
cout << "DestoryMem: i = " << i << endl;
p->~T(); //调用T类型的析构函数
break;
}
//...
编译:
可见,p->~T()是正确的,报错的是ret->T()。这是因为在一个类对象构建完毕后我们不能在调用该对象的构造函数,将该内存空间强制转化为类类型也是如此,c++语法上这样要求,所以当编译器遇到”ret->T()”语句时会将T()看做是对象的一个成员函数,所以报错找不到该成员。析构函数则不同,我们可以在对象构建完毕后手动调用它,所以编译正确。
为了能够调用T类型的构造函数,这就需要调用placement new()函数,也就是定位new:new操作默认是从堆空间申请内存并返回,调用placement new则是返回用于指定的内存的地址,可以理解为在指定的内存地址中执行new操作,通过该地址我们就可以调用对象的构造函数了:
if (!m_used[i])
{
ret = reinterpret_cast<T* >(m_mem) + i;
//ret->T(); //error
ret = new(ret)T(); //在ret指定的地址中执行new操作,并调用该类型的构造函数
cout << "CreateMem: i = " << i << endl;
m_used[i] = true;
break;
}
main()函数:
int main(void)
{
MemPoolManage<A, 5> Amem;
A* a = Amem.CreateMem();
Amem.DestoryMem(a);
return 0;
}
编译运行: