谈C++内存管理与智能指针

谈及C/C++令人头疼的就是指针和内存管理了,可怕的内存泄漏,繁琐的内存管理。好在C98提供了标准支持了C++的一个模板类auto_ptr,还有C++11对auto_ptr的摒弃,以及boost库提供的一系列scoped_ptr,shared_ptr,weak_ptr,unique_ptr等智能指针,貌似都对C++的内存管理提出了解决方案,还专门有scoped_array等来管理动态数组,对C++程序员来说真是福音了。既然说到智能指针,那就免不了提一个名词:“引用计数”,这也是众多编译型语言所采用的方案了!当然对JAVA和C#那些语言而言,GC帮我们处理,就不讨论了!既然有“引用计数”,那就要涉及“强引用”,“弱引用”,“循环引用”了,当然,这些名词不是我在C++里所获取到的,而是OC!OC与C++不同,类对象的实例,全部都是通过指针引用,而在OC的MRC编译环境下,对OC代码的内存管理,便是通过和C++类似的手段去释放内存,如声明retain,release方法等,甚至还有autorelease!用过cocos2d-x引擎的都知道,该引擎也是支持autorelease的,这是为何?那就要说说cocos2d了,这是iOS的一款引擎,OC编写,自然有autorelease不足为奇,但是cocos2d-x跨平台,C++编写,同样支持autorelease,那么必然,肯定需要借助引用计数!而使用过OC的都清楚,在OC的回调方案中,常用的有delegate即代理,block等方式实现,那么对于block而言,最容易出现的便是scope reference即循环饮用,这样一来,对象内存无法释放,内存泄漏在block中成为必然!然而在WWDC新推出ARC环境后,OC开始智能化,再也不需要手动release了!但是OC是编译型语言,没用GC,ARC的机制实属于编译器的支出,ARC代码,编译器会自动分析,并在合适地方,添上release等释放内存的指令!但是ARC并不能解决循环引用!回到那三个引用,同样拿OC与C++比较。在OC中有关键字,strong(ARC缺省的变量类型),weak等,分别表示强引用,弱引用,而boost智能指针有shared_ptr,weak_ptr分别代表强引用与弱引用,这是何其类似!而且其内部原理也异常相似,内存块被强引用,引用计数器累加,当且仅当引用计数变为0时,内存释放,而被弱引用,即使引用计数器不为0,也可能内存被释放!而这对于两者都是类似的!同一块内存区域,强引用具有所有权,只要强引用不取消,内存即被占用,而弱引用只具备访问权,他不能控制内存的释放,即使其计数器为0,也可能不会释放内存!然而对该两种机制来说,弱引用都是解决循环引用的最佳选择!再言C++的智能指针,其实质是类,而且是属于类模板,我们知道,类模板是不能用来实例化对象的,只有当类模板实例化称为模板类,才可再次实例化产生对象!而这都是在编译期进行的!编译器在需要对类模板编译时,首先第一次实例化,生成相应的模板类,再由该模板类生成相应的实例对象,那么自然,该方式就减慢了编译速度!值得一提的是,对于类模板编写,都采用类的声明与方法定义放在一个.h中,至于原因,可以查看boost资料!若分开cpp和h,很容易造成编译出错!而OC呢,其ARC也属于编译时确定,同样需要在编译时确定释放内存的时机,自动添加指令,这对编译来说,同样是需要耗费时间的!然而,C++的智能指针却没有那么完美!因为这些智能指针,实际上也是需要作用域来支持的,只有智能指针的生命周期结束时,析构函数才被调用,内存才被考虑释放需要释放!这对于内存释放来说,或许是有延迟的!而且智能指针是基于栈上的智能,也就是说,他们都是栈声明,因为只有栈上的对象或者变量,才由系统管理,析构才能自动被调用!那么很可怕的发生了:
shared_ptr *ptr = new shared_ptr(new int(123)); 

什么鬼,智能指针对象也是在堆上。。。当然,这是智能指针的误用,但是对于新手而言,很可能这样写!那么在这种情况下,还是避免不了delete语句,也就是说,内存的管理,还是交给了程序员们!所以这种情况下,智能指针不完美了!其次,如果逻辑中又再次获取了指针呢?重复释放又发生了!那么如下代码:
 int *ptr = new int(123);shared_ptr _smart_ptr(ptr); delete ptr; 

完了,在初始化对象 _smart_ptr后,内存被释放了,这个对象生命周期一结束,析构被调用,需要再次释放内存,帮当。。。后果可想而之,程序崩溃必不可少!因而C++智能指针并不那么完美,当然这也可以算得上是误用了!因此,智能指针是不应该和普通指针同时出现的,在使用智能指针时,逻辑不能再拿到普通指针的拥有权,所以有可能C++的代码应该这样写:
shared_ptr _smart_ptr(new int(123));

 栈上的智能指针object,逻辑不拥有指针!也许这是合理的!也就是说,既然程序员决定要使用智能指针,那么智能好好的遵守游戏规则!但是C++程序员即使遵守了规则,又会碰到新的问题。C++是面向对象的语言,最基本的多态是OOP必须要满足的!普通指针,实现多态异常简单!尤其对于向上转型,更是如此!如基类A有子类B,那么如下的代码将是成功的,并且安全的!
A *a = new B; 

但是向下转型则不然,如基类A有子类B,同时有子类C,那么以下代码将是失败的:
 A *a = new B; 
C *c = (C*)a; 

该转型为向下转型,是失败的!但是C++11标准,提供了多种转型方法,其中就包括向下转型,dynamic_cast便可安全向下转型,但是也需要程序员判断是否合法,这对C++程序员而言是基本功了,指针使用先判空… 但是使用智能指针,要完成转型,说来向上转型也是方便。如基类A有子类B,那么如下即可: shared_ptr b(new B); shared_ptr a = b; 是不是C++收录boost的智能指针到STL也是很美妙呢?然而,向下转型怎么办,那么就要采用新的方法:dynamic_pointer_cast!如此一来,这经历了多少层包装!必然影响C++的执行效率!而OC在编译时确定的释放内存的时机,是不是显得更加合理和高效呢?所以,C++的智能指针,并不智能,要想真正实现智能花费的代价也是非常大的,不管引用计数也好,采用树的机制也罢(有点像GC的堆了,GC对堆的维护,代价也不小哦,这也是java的eclipse等软件那么慢的原因之一吧,所以JAVA做大型游戏,很可怕的。。。)。要想真正做到高效,还得交由程序员处理,这同样也是对C++程序员的考验了,但是这也正是C++程序员的价值所在之一,优美高效的C++代码,还是很有价值的!当然STL是高效的,是强大的库,该用还是得用哦~

你可能感兴趣的:(C++,C语言,STL,oc,智能指针)