RAII 与Pimpl 源地址http://tech.uc.cn/?p=851程序员
RAIIsegmentfault
RAII是Bjarne Stroustrup教授用于解决资源分配而发明的技术,资源获取即初始化。函数
RAII是C++的构造机制的直接使用,即利用构造函数分配资源,利用析构函数来回收资源。布局
咱们知道,在C/C++语言中,对动态分配的内存的处理必须十分谨慎。在没有RAII应用的状况下,若是在内存释放以前就离开指针的做用域,这时候几乎没机会去释放该内存,除非垃圾回收器对其管制,不然咱们要面对的将会是内存泄漏。性能
举个例子来讲明下RAII在内存分配方面的使用。this
这是典型的C风格代码,没有应用RAII。
所以值得注意的是,destroy_bytearray必须在退出做用域前被调用。
然而在复杂的逻辑设计中,程序员每每要花大量的精力以确认全部在该做用域分配的ByteArray获得正确的释放。spa
相形之下,C++运行机制保证了栈上对象一旦即将离开做用域,其析构函数将被执行,给予了释放资源的时间。注意,在堆分配的对象必须调用delete来结束其生命。设计
C++11 STL中的std::unique_ptr可用于控制做用域中的动态分配的对象。
譬如:指针
函数bar()只是增长了一行,但强壮了不少,函数bar()执行完或者有异常抛出时,holder总会被析构,从而ba或被delete。code
下面是ByteArray的Ada实现:
-- lib.ads withinterfaces; withAda.Finalization; packagelib is typeuchars isarray(positive range<>)ofinterfaces.unsigned_8; typeuchars_p isaccessuchars; typeByteArray isnewAda.Finalization.Limited_Controlled withprivate; functionCreate(length:integer)returnByteArray; private typeByteArray isnewAda.Finalization.Limited_Controlled withrecord length:integer; data:uchars_p; endrecord; overriding procedureInitialize(This:inoutByteArray); overriding procedureFinalize(This:inoutByteArray); endlib; -- lib.adb withAda.Unchecked_Deallocation; packagebodylib is useAda.Finalization; functionCreate(length:integer)returnByteArray is begin iflength<0then put_line("Create"); returnByteArray'(Limited_Controlled with length => length, data=> new uchars(1..length)); end if; return ByteArray'(Limited_Controlled withlength=>0,data=>null); endCreate; overriding procedureInitialize(This:inoutByteArray)is begin put_line("Initialize"); this.length:=0; this.data:=null; endInitialize; overriding procedureFinalize(This:inoutByteArray)is procedurefree isnewAda.Unchecked_Deallocation(uchars,uchars_p); begin put_line("Finalize"); if(this.data/=null)then free(this.data); endif; endFinalize; endlib; -- main.adb withlib; uselib; proceduremain is K:ByteArray:=Create(10240); C:ByteArray; begin null; endmain;
– 输出以下
./main
Create
Initialize
Finalize
Finalize
另外一种状况是对I/O资源的处理,当咱们再也不使用资源时,必须将资源归还给系统。
下面例子来自 wikipedia的RAII条目:
在write_to_file函数中,RAII做用于std::ofstream和std::lock_guard,从而保证了函数write_to_file在返回时,lock和file总会调用自身的析构函数,对于lock而言,它会释放mutex,而file则会close。
Pimpl
Pimpl(pointer to implementation),是一种应用十分普遍的技术,它的别名也不少,如Opaque pointer, handle classes等。
wikipedia上已经对其就Ada、C和C++举例,这里不做举例。
我的认为,Pimpl是RAII的延展,籍由RAII对资源的控制,把具体的数据布局和实现从调用者视线内移开,从而简化了API接口,也使得ABI兼容变得有可能,Qt和KDE正是使用Pimpl来维护API的一致性,另外也为惰性初始化提供途径,以及隐式共享提供了基础。
我在设计代码时也会考虑使用Pimpl,但不是必然使用,由于Pimpl也会带来反作用,主要有两方面
Pimpl指针致使内存空间开销增大
类型间Pimpl的访问须要较多间接的指针跳转,甚至还用使用friend''来提高访问权限,如如下代码中,Teacher能够访问Student的Context。
尽管如此,我我的仍是在面向开发应用的接口中会尽可能使用Pimpl来维护API和ABI的一致性,除非Pimpl会引发显著的性能降低。