RAII 与Pimpl 源地址http://tech.uc.cn/?p=851
RAII
RAII是Bjarne Stroustrup教授用于解决资源分配而发明的技术,资源获取即初始化。
RAII是C++的构造机制的直接使用,即利用构造函数分配资源,利用析构函数来回收资源。
我们知道,在C/C++语言中,对动态分配的内存的处理必须十分谨慎。在没有RAII应用的情况下,如果在内存释放之前就离开指针的作用域,这时候几乎没机会去释放该内存,除非垃圾回收器对其管制,否则我们要面对的将会是内存泄漏。
举个例子来说明下RAII在内存分配方面的使用。
这是典型的C风格代码,没有应用RAII。
因此值得注意的是,destroy_bytearray必须在退出作用域前被调用。
然而在复杂的逻辑设计中,程序员往往要花大量的精力以确认所有在该作用域分配的ByteArray得到正确的释放。
相形之下,C++运行机制保证了栈上对象一旦即将离开作用域,其析构函数将被执行,给予了释放资源的时间。注意,在堆分配的对象必须调用delete来结束其生命。
C++11 STL中的std::unique_ptr可用于控制作用域中的动态分配的对象。
譬如:
函数bar()只是增加了一行,但强壮了很多,函数bar()执行完或者有异常抛出时,holder总会被析构,从而ba或被delete。
下面是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会引起显著的性能下降。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。