内存池:为了解决频繁开辟内存、释放内存的效率问题和碎片问题。
效率问题:因为要每次开辟内存释放内存都要从用户态切换到内核态,状态的转换需要时间, 需要尽可能地避免。
碎片问题:由于系统分配内存无法得知未来的内存分配布局,所以不可避免地会产生内存碎片。
对象的开辟:分两步:
1、开辟空间:调用operator new()函数
2、调用构造函数来对所开辟的空间初始化。
通用内存池的实现:
申请一块大的内存区域,然后自主分配内存的使用,就可以避免上述问题的发生。
内存池应和对象分离,尽可能地,我们应使用面向对象的思想,所以,我们可以将内存池封装成类;结合两种 说法就是:将管理交给内存池来实现,对外只需要提供两个接口来模拟new和delete关键字。
为了方便内存管理,这里最好选用静态链表的方式进行组织,因为内存随时需要变动,所以需要使用链表,但 又是我们管理已知大小和方式的类型,所以可以使用数组的方式存储,,相结合最好的管理方式就是静态链表。
静态链表的内存布局:多个数据域加指针域构成的结点域所组成的数组来管理,在内存池中应该模拟真实内存分配:即 分配出去的内存交给用户来管理,而未使用部分则交给内存池来管理,本来应该将内存池分为这两部分,而仔细思考下其 实由内存池管理的只有未使用部分,所以,只需一个指针指向未使用部分的头即可,还有要做的就是当用户delete时需要 可以把还回来的内存重新链接回去。
数据结构设计完毕后到了函数设计部分了:
1、alloc()函数:首先,开始,判断未使用部分的指针是否为NULL,若为 NULL,则需要开辟空 间,并以传入的数据结构大小加上指针域大小进行初始化。
2、delalloc()函数:模拟头插或者尾插的实现方式,将外部的一个内存区域链入到未使用的部分。
代码如下:
#include<iostream>
using namespace std;
const int MEM_SIZE = 2;
template<typename T>
class MEM_POOL
{
public:
MEM_POOL():pool(NULL){}
void* alloc(size_t size)
{
if(pool == NULL)
{
pool = (Node*) new char[(size + 4) * MEM_SIZE];
Node* pCur = pool;
for(;pCur < pool + MEM_SIZE - 1;++pCur)
{
pCur->pnext = pCur + 1;
}
pCur->pnext = NULL;
}
Node* rt = pool;
pool = pool->pnext;
return rt;
}
void dealloc(void* ptr)
{
if(ptr == NULL)
return;
((Node*)ptr)->pnext = pool;
pool = (Node*)ptr;
}
private:
class Node
{
public:
Node(T val = T()):mdata(val),pnext(NULL){}
public:
T mdata;
Node* pnext;
};
Node* pool;
};
class Stu
{
public:
Stu(char* name,int mage):mname(new char[strlen(name)+1]),age(mage)
{
strcpy(mname,name);
}
void* operator new(size_t size)
{
return mpool.alloc(size);
}
void operator delete(void* ptr)
{
mpool.dealloc(ptr);
}
~Stu()
{
delete []mname;
mname = NULL;
}
private:
char* mname;
int age;
static MEM_POOL<Stu> mpool;
};
MEM_POOL<Stu> Stu::mpool;
int main()
{
Stu* pstu1 = new Stu("zhangsan",10);
Stu* pstu2 = new Stu("lisi",11);
Stu* pstu3 = new Stu("wangwu",12);
Stu* pstu4 = new Stu("maliu",13);
delete pstu1;
delete pstu2;
delete pstu3;
delete pstu4;
int *p = (int*)malloc(4);
double* p2 = (double*)malloc(8);
int *p3 = (int*) malloc(8);
int a = (int)p;
a += 1;
p = (int*)a;
free(p);
free(p2);
free(p3);
return 0;
}
free的内存释放机制:malloc需要调用底层的接口,系统调用硬件资源开辟内存,malloc接收用户输入的内存大小,系统当然也 会得到,所以,系统会维护一个数据结构,维护返回的地址和开辟的大小,而free时,拿地址和数据结构的存储里面匹配,如 果匹配了,就把size大小的大小释放,如果未匹配到,就报错,程序崩溃。
使用单例模式实现内存池:
因为每种类型的内存池只需要存在一个来管理即可,一个内存池就可以管理任意多个同类型数据,所以,通过模板实例 化的类都只需要实例化生成一个对象即可,这种情况,用单例模式恰到好处。
代码实现:
#include<iostream>
using namespace std;
const int MEM_SIZE = 2;
template<typename T>
class MEM_POOL
{
public:
static MEM_POOL* getInstance()
{
return &mempool;
}
void* alloc(size_t size)
{
if(pool == NULL)
{
pool = (Node*) new char[(size + 4) * MEM_SIZE];
Node* pCur = pool;
for(;pCur < pool + MEM_SIZE - 1;++pCur)
{
pCur->pnext = pCur + 1;
}
pCur->pnext = NULL;
}
Node* rt = pool;
pool = pool->pnext;
return rt;
}
void dealloc(void* ptr)
{
if(ptr == NULL)
return;
((Node*)ptr)->pnext = pool;
pool = (Node*)ptr;
}
private:
MEM_POOL():pool(NULL){}
MEM_POOL(const MEM_POOL<T>&);
class Node
{
public:
Node(T val = T()):mdata(val),pnext(NULL){}
public:
T mdata;
Node* pnext;
};
Node* pool;
static MEM_POOL<T> mempool;
};
template<typename T>
MEM_POOL<T> MEM_POOL<T>::mempool;
class Stu
{
public:
Stu(char* name,int mage):mname(new char[strlen(name)+1]),age(mage)
{
strcpy(mname,name);
}
void* operator new(size_t size)
{
return mpool->alloc(size);
}
void operator delete(void* ptr)
{
mpool->dealloc(ptr);
}
~Stu()
{
delete []mname;
mname = NULL;
}
private:
char* mname;
int age;
static MEM_POOL<Stu>* mpool;
};
MEM_POOL<Stu>* Stu::mpool = MEM_POOL<Stu>::getInstance();
int main()
{
Stu* pstu1 = new Stu("zhangsan",10);
Stu* pstu2 = new Stu("lisi",11);
Stu* pstu3 = new Stu("wangwu",12);
Stu* pstu4 = new Stu("maliu",13);
delete pstu1;
delete pstu2;
delete pstu3;
delete pstu4;
MEM_POOL<Stu>* spool1 = MEM_POOL<Stu>::getInstance();
MEM_POOL<Stu>* spool1 = MEM_POOL<Stu>::getInstance();
MEM_POOL<Stu>* spool1 = MEM_POOL<Stu>::getInstance();
MEM_POOL<Stu>* spool1 = MEM_POOL<Stu>::getInstance();
/*int *p = (int*)malloc(4);
double* p2 = (double*)malloc(8);
int *p3 = (int*) malloc(8);
int a = (int)p;
a += 1;
p = (int*)a;
free(p);
free(p2);
free(p3);
return 0;*/
}
运算符重载之智能指针:
堆栈的区别:
栈:系统开辟 系统释放
堆:手动开辟 手动释放
智能指针:类的析构函数就是C++设计的由系统自动释放堆上资源的方式,所以,我们可以通过构造类,来实现系统自动回 收堆上的内存。
#include<iostream>
using namespace std;
int Compare(int a)
{
int* p = new int();
if(*p == a)
{
return 0;
}
delete p;
return a;
}
template<typename T>
class SmartPtr
{
public:
SmartPtr(T* ptr = NULL):mptr(ptr){}
SmartPtr(const SmartPtr&rhs):mptr(rhs.mptr)//拷贝构造容易造成重复析构的问题:
//解决办法1:管理权唯一(只有一个对象指向对应内存块,其他的一旦调用拷贝构造就指向NULL)
{
rhs.Release();
}
SmartPtr<T>&operator=(const SmartPtr<T>& rhs)
{
if(this != &rhs)
{
delete mptr;
mptr = rhs.mptr;
rhs.Release();
}
return *this;
}
T&operator*()
{
return *mptr;
}
T* operator->()
{
return mptr;
}
~SmartPtr()
{
delete mptr;
mptr = NULL;
}
private:
void Release()const
{
(T*)mptr = NULL;
}
T* mptr;
};
class Test
{
public:
Test(int a):ma(a){}
void Show()
{
cout<<ma<<endl;
}
private:
int ma;
};
int main()
{
int* p = new int;
SmartPtr<int> sp1(p);
SmartPtr<int> sp2(sp1);
sp1 = sp2;
Test* ptest = new Test(30);
ptest->Show();
SmartPtr<Test>sp3(ptest);
sp3->Show();//指向运算符是一个单目运算符,因为其无法判断右边的类型是否是一个变量还是一个函数,所以返回值只和左边的参数有关
return 0;
}
友元关系:
1、函数友元