内存管理与智能指针

来源:互联网 发布:清华it培训学校 编辑:程序博客网 时间:2024/05/02 18:36

内存管理与智能指针

 

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

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

 

 

 

原创粉丝点击