天天看点

内存管理与智能指针一、   基础准备二、   需求分析三、   实现

内存管理与智能指针

        作为一名合格的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*/
           

        当然嫌构造器形式太复杂的话,我们可以用个函数将其简化,这都不是关键。