内存管理与智能指针
作为一名合格的C++程序猿,管理内存是不可逃避的现实。像java,python等这些面向对象的语言来说,它们有垃圾回收机制,不需要程序员来管理内存。然而为了追求效率的C++没有实现这一机制。那么我们就必须借助各种技术来解决内存泄露这一头疼的事情了。
C++中内存泄露是防不胜防的,因为毕竟人的能力是有限的。那么我们就必须借助一些技术来减少出错的可能。能存管理说的主要是管理由malloc,new分配的堆空间。而操作这些空间的句柄就是指针。归根结底就是对指针的操作。以避免造成野指针,悬垂指针以及内存泄露的情况,看了一些智能指针的例子以及STL后,一直想要动手自己写一套内存管理的工具。
一、 基础准备
1.内存分配方式
要想实现内存管理,首先要了解分配的方式:(1)变量分配,(2)常量的分配。
1. 变量的分配(class T)
T * pt = new T()
T* pt = new T(const T&t)
这两种方式分别用到了默认构造函数,以及拷贝构造函数,对于带形参的构造函数的调用有待使用变长参数列表实现。
2. 常量的分配(class)
const T* pt = new const T()
const T* pt = new const T(const T& t)
2.内存分配表
通过维护一个内存分配表来实现对已分配内存的动态跟踪,对于内存分配表也有两种实现方案:(1)全局分配表;(2)类型分配表。
1. 全局分配表
指的是整个程序共有同一份分配表,那么分配表。这里会有一个技术问题,分配的内存类型并不统一,那么存储形式只能是如下两种情况:
a. map<void*,int*>
b. map<Object*,int*>
对于a我们无法通过分配表来实现对内存的释放,C++无法delete空指针。那么我们只能在智能指针的对象中对其进行释放(因为它了解自己的类型)。但是这有一个问题,当我们分配了一段内存,但没有任何智能指针指向它时,它将无法释放。有一个解决方案,那就是在构造时内部默认生成一个智能指针指向它,这回带来不必要的内存开销。或者强制要求用户必须用至少一个职能指针指向分配器分配的内存。不仅如此,在性能上整个程序共有一个分配表会造成分配表size异常的庞大,影响查询速度,同时影响多线程程序的速度。所以此方案不可取。
对于Object是一个公有子类,任何其他类必须继承它,它有一个虚析构函数,这样可以实现统一销毁对象。然而C++中没有这个公共子类,那么适用范围为:(1)自定义类型;(2)包装已有类型。
class Object{
public:
Object(){}
virtaul ~Object() = 0;
}
template<classT>
class Dynamic:public Object{
public:
Dynamic();
Dynamic(const T&);
Dynamic(const Dynamic&);
T& self();
private:
T data;
};
这样包装之后可以让所有类型从某种意义上来说继承自Object.对于处理自定义类型那就更加容易了,只需要一个公共基类,当然使用(2)同样可以处理自定义类型。
2. 类型分配表
指的是一个类型一个分配表,如int,string等每个类型一个分配表。这样的话我们智能指针可以跟踪内存的释放,同时当出现遗漏时,如没有任何智能指针指向该段内存,分配器可以负责释放(因为此时分配器知道自己分配的具体类型)。同时这样分配表也会相对较短,查找效率更高。此时的分配表为:
map<T*,int*>
3. python思想迁移
我们可以想想,其实所有分配在栈和静态存储区的存储空间都是由一个变量来标识,这是由系统自己管理的内存空间,而堆上分配的空间在每次运行期都是不定的,也就是不是编译期决定的,所以只能用指针来标识。那么我们是否可以将这种指针标识的内存空间标量化,对象化呢?其实是可以的,事实上python就是这么做的。通过采用引用计数的方式来管理这段内存。我们实现在此:C++实现Python变量。
二、 需求分析
除了上面的python思想实现了之外,我们还需要实现类型分配表这种方法。我们需要的功能有:
1. 每个类型有个统一的内存分配器alloctor负责分配内存,同时alloctor维护者一个内存分配表map<T*,int*>(int*用于引用计数)。
2. 每种类型有个成员生成器Dynamic<T>::Construct()。
3. 智能指针跟踪内存分配表的引用计数。
4. 当智能指针与空间脱离绑定(生命周期结束/指向其他空间),检测到引用计数为0时,调用分配器的销毁函数destroy(T*)对其 进行销毁。当程序结束时分配器会检查遗漏的空间,并将其一并销毁。
5. 程序员被建议不要显示使用malloc/free,new/delete关键字,如果使用了,那么这部分资源的释放你必须负责。
6. 这种智能指针的好处在于,它可以指向静态数据和栈数据。而依据python思想设计的Obj_ptr永远都不会和这两类数据邦定。两者的差异还是较为明显的。基于类型分配表方案的设计,智能指针仍然具有指针语义,它可以是NULL,而基于python的设计思想Obj_ptr已经具有值语义。
三、 实现
1.Alloctor(分配器)
template<class T>
class Alloctor{
public:
typedef typename map<T*,int*>::value_type value_type;
typedef typename map<T*,int*>::iterator iterator;
public:
Alloctor(){}
//template<class T>
T* Construct()
{
T *get = new T();
T *p = get;
int *useCount = new int(0);
obj.insert(value_type(p,useCount));
return get;
}
//template<class Ty>
T* Construct(const T &t)
{
T *get = new T(t);
T *p = get;
int *useCount = new int(0);
obj.insert(value_type(p,useCount));
return get;
}
~Alloctor();
int* getUseCount(T* oj);
bool getElement(iterator iter,T* oj);
void destroy(T* oj);
int ListLen(){return obj.size();}
private:
map<T*,int*> obj;
};
template<class T>
Alloctor<T>::~Alloctor()
{
std::cout<<"Alloctor<T>::~Alloctor()"<<std::endl;
typename map<T*,int*>::iterator iter = obj.begin();
typename map<T*,int*>::iterator end = obj.end();
for(;iter != end;iter++)
{
delete iter->first;
delete iter->second;
}
}
template<class T>
int* Alloctor<T>::getUseCount(T* oj)
{
iterator iter = obj.find(oj);
if(iter == obj.end())return NULL;
else{
return iter->second;
}
}
template<class T>
bool Alloctor<T>::getElement(iterator iter,T* oj)
{
iter = obj.find(oj);
if(iter == obj.end())return false;
else{
return true;
}
}
template<class T>
void Alloctor<T>::destroy(T* oj) //public is not very security.
{
std::cout<<"Alloctor<T>::destroy(T* oj)"<<std::endl;
int *count = getUseCount(oj);
if(count != NULL)
{
(*count)--;
if(*count == 0)
{
obj.erase(oj);
delete oj;
}
}
}
2.成员生成器Dynamic<T>::Construct()
template<class T,class alloc = Alloctor<T> >
class Dynamic{
public:
static T* Construct()
{
return alloctor.Construct();
}
static T* Construct(const T &t)
{
return alloctor.Construct(t);
}
//private:
static alloc alloctor;
};
template<class T,class alloc> alloc Dynamic<T,alloc>::alloctor = Alloctor<T>();
3.智能指针
template<class T>
class Obj_ptr{
public:
typedef typename map<T*,int*>::value_type value_type;
typedef typename map<T*,int*>::iterator iterator;
public:
Obj_ptr();
Obj_ptr(const Obj_ptr &ptr);
Obj_ptr(T *ptr);
Obj_ptr& operator=(const Obj_ptr &ptr);
Obj_ptr& operator=(T *ptr);
bool operator==(const T *ptr){return p == ptr;}
bool operator==(const Obj_ptr &ptr){return p == ptr.p;}
T& operator*(){return *p;}
T*& operator->(){return p;}
~Obj_ptr();
void Destory();
private:
T *p;
};
//
/*Obj_ptr*/
template<class T>
Obj_ptr<T>::Obj_ptr()
{
std::cout<<"Obj_ptr()."<<std::endl;
p = NULL;
}
template<class T>
Obj_ptr<T>::Obj_ptr(const Obj_ptr &ptr)
{
std::cout<<"const Obj_ptr(Obj_ptr &ptr)."<<std::endl;
p = ptr.p;
int *count = Dynamic<T>::alloctor.getUseCount(p);
if(count != NULL)
{
(*count)++;
}
else{
//p = NULL;
}
}
template<class T>
Obj_ptr<T>::Obj_ptr(T *ptr)
{
std::cout<<"Obj_ptr(T *ptr)."<<std::endl;
p = ptr;
int *count = Dynamic<T>::alloctor.getUseCount(p);
if(count != NULL)
{
(*count)++;
std::cout<<"count:"<<*count<<std::endl;
}
else{
//p = NULL;
}
}
template<class T>
Obj_ptr<T>& Obj_ptr<T>::operator=(const Obj_ptr &ptr)
{
std::cout<<"Obj_ptr operator=(Obj_ptr &ptr)."<<std::endl;
if(&ptr != this)//not give self to self.
{
int *count = Dynamic<T>::alloctor.getUseCount(p);
if(count != NULL)
{
if(*count == 1)
{
Destory();
}
else{
(*count)--;
}
std::cout<<"count--:"<<*count<<std::endl;
}
p = ptr.p;
count = Dynamic<T>::alloctor.getUseCount(p);
if(count != NULL)
{
(*count)++;
std::cout<<"count++:"<<*count<<std::endl;
}
else{
//p = NULL;
}
}
return *this;
}
template<class T>
Obj_ptr<T>& Obj_ptr<T>::operator=(T *ptr)
{
std::cout<<"Obj_ptr operator=(T *ptr)."<<std::endl;
int *count = Dynamic<T>::alloctor.getUseCount(p);
if(count != NULL)
{
if(*count == 1)
{
Destory();
}
else{
(*count)--;
}
std::cout<<"count--:"<<*count<<std::endl;
}
p = ptr;
count = Dynamic<T>::alloctor.getUseCount(p);
if(count != NULL)
{
(*count)++;
std::cout<<"count++:"<<*count<<std::endl;
}
else{
//p = NULL;
}
return *this;
}
template<class T>
Obj_ptr<T>::~Obj_ptr()
{
std::cout<<"Obj_ptr<T>::~Obj_ptr()"<<std::endl;
Destory();
}
template<class T>
void Obj_ptr<T>::Destory()
{
Dynamic<T>::alloctor.destroy(p);
}
/*Obj_ptr*/
当然嫌构造器形式太复杂的话,我们可以用个函数将其简化,这都不是关键。