c++中的智能指针

内存管理是c++中比较棘手的问题。轻则是内存泄漏,重则是野指针、访问越界而崩溃,我们必须小心翼翼的对待指针。c++写多了,有时候会怀念java,c#这种有垃圾回收机制的语言,它们写出的代码是多么的干净优雅。

以前c++标准库里只有一个auto_ptr的智能指针,它的出现让我眼睛为之一亮,原来c++代码还可以这样管理指针啊。但是auto_ptr适应的场景有限,还有自己本身管理权转移晦涩的问题。

c++的准标准库boost为此准备了一组智能指针,scoped_ptr、socped_array、shared_ptr、shared_array、weak_ptr、intrusive_ptr。开源的浏览器chromium也有自己的智能指针解决方案linked_ptr、scoped_ptr、scoped_refptr、WeakPtr。c++ 11也增加了几个更加实用,功能更加强大的智能指针unique_ptr、shared_ptr。这么多智能指针让人看起来眼花了,不知道用哪种智能指针才是最合适的。

c++中的智能指针

auto_ptr

auto_ptr智能指针应该只用于防止”被异常抛出时发生资源泄露”:

在使用auto_ptr管理指针之后,只要auto_ptr超出自己的作用域,就会回收指针的内存:

auto_ptr的指针管理器是可以转移的,但是auto_ptr同时最多只能管理一个指针。比如把一个指针交个auto_ptr管理,那么auto_ptr之前管理的指针会被回收:

auto_ptr还可以在函数间传递:

auto_ptr语义本身就包含了拥有权,如果你无意转交所有权,就不要用在函数的参数列表和函数返回值中。下面的例子中函数只是想打印出auto_ptr指向的值,却得到了对象的所有权:

虽然我们可以用使用const来保留所有权,防止意外的转移,但是这个管理权转移还是太微妙,很容易出问题。再加上它不满足stl容器对其元素的要求,所以最新的c++标准已经不推荐使用auto_ptr了。

unique_ptr

unique_ptr是c++标准中auto_ptr的替代品,unique_ptr不可拷贝或复制,转让所有权,用起来更加安全。unique_ptr也是boost中scoped_ptr,scoped_array的进化,可以同时管理一般指针和数组指针:

因为unique_ptr不能拷贝复制,所以给别的函数传递参数时,需要unique_ptr.get()获得原始的指针传递给函数,直接传递unique_ptr类型是不行的:

如果觉得给函数传递原始的指针还不是太危险,我们可以用std::move显式的所有权转移:

但是可以从一个函数中直接返回unique_ptr:

unique_ptr之间的初始化和赋值是不允许的,同样借助了std::move,又是可以的:

unique_ptr还可以与跟stl中的各种容器合作,这是auto_ptr无法做到的:

unique_ptr还可以定制删除器,这点又拓展了unique_ptr用途,它不仅可以管理指针,还可以管理HANDLE等非指针对象。定制删除器就是在析构的时候不是简单的delete所管理的指针,你可以定制自己的操作:

可见定制了自己的删除器,unique_ptr不再删除指针了。

shared_ptr

shared_ptr是有引用计数的智能指针,可以被自由的拷贝和赋值,当引用计数为0时自动销毁,使用起来跟java中普通对象的垃圾回收类似,除了循环引用问题,它几近完美。

shared_ptr当作参数传递给函数没有auto_ptr的管理权转移问题,因为shared_ptr指针管理权是共享的,只不过会增加引用计数罢了,也无须像unique_ptr那样需要std::move辅助。总之使用shared_ptr非常自然,方便,安全。

shared_ptr很好的消除了我们对delete的使用,但是我们初始化的时候还是用到的new。如果你觉得这是种不对称的话,可以用make_shared消除对new的使用。

跟unique_ptr一样,shared_ptr也可以自然的跟stl中的容器配合使用。shared_ptr定制删除器比unique_ptr更加简单。

weak_ptr

weak_ptr是为了解决shared_ptr循环引用问题而引入的一种智能指针。weak_ptr不具备一般指针的特性,它仅仅是为了协助shared_ptr,所以被设计为从一个shared_ptr或者另一个weak_ptr对象中构造,获得shared_ptr的观察权。weak_ptr没有获得指针的共享权,所以不会引起指针引用计数的变化。循环引用问题:

这两个shared_ptr拿着彼此的引用导致了谁都无法释放资源。这个时候我们就需要用weak_ptr将强引用改变成弱引用,来打破这种死结。当真正需要强引用的地方,再调用weak_ptr的lock函数获得:

boost中的智能指针

其实c++ 11标准库中的智能指针涵盖了boost库中智能指针的绝大部分功能,几乎没有任何理由再使用boost库中的智能指针了。两个库之间的智能指针接口也类似,只是名字有些不同。boost库中的scoped_ptr和scoped_array对应着标准库的unique_ptr。shared_ptr和shared_array对应标准库的shared_ptr。weak_ptr对应着标准库的weak_ptr。

boost库中还有个intrusive_ptr,一种”侵入式”的引用计数指针,它实际并不提供引用计数功能,而是要求被存储的对象自己实现引用计数功能,并提供intrusive_ptr_add_ref和intrusive_ptr_release函数接口供boost::intrusive_ptr调用。因为unique_ptr、shared_ptr足够灵活强大了,intrusive_ptr很少使用。

chromium中的智能指针

chromium中的智能指针有linked_ptr、scoped_ptr、scoped_refptr、WeakPtr几种。scoped_ptr、scoped_refptr、WeakPtr依次对应标准库中的unique_ptr、shared_ptr、weak_ptr。

chromium有个独特的linked_ptr,它的设计初衷是某些情况下scoped_ptr<>与stl容器不适应的替代品。linked_ptr也不是线程安全的,chromium本身建议用ScopedVector替代。

发表评论

电子邮件地址不会被公开。 必填项已用*标注