Boost库——auto_ptr、scoped_ptr、share_ptr、weak_ptr小结

来源:互联网 发布:linux重启 编辑:程序博客网 时间:2024/06/15 09:02

       智能指针是C++11标准中新添的内容,是从boost库中移植到标准库的。最近研究源码遇到智能指针的应用场景越来越多,为了后续研究方便,借此机会边实践边总结一下。

1、auto_ptr

         auto_ptr是现在标准库里面一个轻量级的智能指针的实现,存在于头文件 memory中,之所以说它是轻量级,是因为它只有一个成员变量(拥有对象的指针),相关的调用开销也非常小。关于auto_ptr的使用,其通过构造函数拥有一个动态分配对象的所有权,然后就可以被当作对象指针来使用,当auto_ptr对象被销毁的时候,它也会自动销毁自己拥有所有权的对象。

        auto_ptr是一个相当容易被误用并且在实际中常常被误用的类。原因是由于它的对象所有权占用的特性和它非平凡的拷贝行为。auto_ptr的对象所有权是独占性的,主要体现在不可能有两个auto_ptr对象同时拥有同一动态对象的所有权。

         auto_ptr的几点注意事项:
               1)auto_ptr不能共享所有权
               2)auto_ptr不能指向数组
               3)auto_ptr不能作为容器的成员
               4)不能通过复制操作来初始化auto_ptr
                     std::auto_ptr<int> p(new int(42)); //OK
                     std::atuo_ptr<int>p = new int(42);//Error
                   这是因为auto_ptr的构造函数被定义了explicit
               5)不要把auto_ptr放入容器

2、scoped_ptr

              scoped_ptr在Boost库头文件scoped_ptr.hpp中。boost::scoped_ptr 用于确保动态分配的对象能够被正确地删除。scoped_ptr 有着与std::auto_ptr类似的特性,而最大的区别在于它不能转让所有权而auto_ptr可以。事实上,scoped_ptr永远不能被复制或被赋值!scoped_ptr 拥有它所指向的资源的所有权,并永远不会放弃这个所有权。scoped_ptr的这种特性提升了我们的代码的表现,我们可以根据需要选择最合适的智能指针(scoped_ptr 或 auto_ptr)。要决定使用std::auto_ptr还是boost::scoped_ptr, 就要考虑转移所有权是不是你想要的智能指针的一个特性。如果不是,就用scoped_ptr. 它是一种轻量级的智能指针;使用它不会使你的程序变大或变慢。它只会让你的代码更安全,更好维护。

          scoped_ptr有以下特点:

        1)不能共享控制权。scoped_ptr不能通过其他scoped_ptr共享控制权,因为在scoped_ptr类的内部将拷贝构造函数=运算符重载定义为私有的;

        2)scoped_ptr不能用在标准库的容器中,因为容器中的push_back操作需要调用scoped_ptr的=运算符重载函数,结果就是会导致编译失败。

       scoped_ptr不允许拷贝和赋值,只能在scoped_ptr所声明的作用域内使用。除了解应用*和箭头访问符->操作,scoped_ptr没有定义其他操作符,不能像普通指针进行++或——等指针算术操作。与普通指针相比,它只有更小的接口,因此使指针的使用更加安全。使用scoped_ptr可以带来以下好处:1)代码更加清晰简单,简单意味着更少的错误;2)没有增加多余的操作,安全且高效,可以获得与原始指针一样的速度。

    与auto_ptr的区别:

    scoped_ptr的用法与auto_ptr几乎一样,大多数情况下可以与auto_ptr相互替换,但二者最本质区别在于指针的所有权。auto_ptr特意被设计为指针所有权是可转移的,而scoped_ptr不能转移指针所有权。二者也有同样的“缺陷”,不能作为容器的元素。auto_ptr是因为它的转移语义,而scoped_ptr是由于不支持拷贝和赋值,不符合容器对元素类型的要求。

3、share_ptr

         shared_ptr是Boost库smart_ptr中最有价值、最重要的智能指针,其主要作用提供一个标准的共享所有权的智能指针。share_ptr与scope_ptr一样包装了new操作符在堆上分配的动态对象,但它实现的是引用计数型的智能指针,可以被自由地拷贝和赋值,在任意的地方共享它,当没有代码使用它时(引用计数为0)才删除被包装的动态分配的对象。share_ptr也可以安全地放到标准容器中,弥补了auto_ptr因为转移语义而不能把指针作为STL容器元素的缺陷。由于shared_ptr是可以拷贝和赋值的,拷贝行为也是等价的,并且可以被比较,因此它可被放入标准库的一般容器(vector,list)和关联容器中(map)。  

        Boost库里面有很多shared_ptr的使用例程,文档里面也列举了许许多多shared_ptr的用途,其中最有用也最常用的莫过于传递动态分配对象,有了引用计数机制,现在可以安全地将动态分配的对象包裹在shared_ptr里面跨越模块跨越线程的边界传递。shared_ptr可以为我们自动管理对象的生命周期。但是shared_ptr 本身不是 100%线程安全的。它的引用计数本身是安全且无锁的,但对象的读写则不是,因为shared_ptr有两个数据成员,读写操作不能原子化,对于多个线程读写同一个对象时需要加锁。根据文档,shared_ptr的线程安全级别和内建类型、标准库容器、string一样,即:

                 1)一个 shared_ptr 实体可被多个线程同时读取;

                 2)两个的 shared_ptr 实体可以被两个线程同时写入,“析构”算写操作;
                 3)如果要从多个线程读写同一个 shared_ptr 对象,那么需要加锁。

4、weak_ptr

         weak_ptr是为配合shared_ptr而引入的一种智能指针来协助shared_ptr工作,它更像shared_ptr的一个助手而不是智能指针,因为它不具有普通指针的行为,没有重载operator*和->。它的最大作用在于协助shared_ptr工作,像旁观者那样观测资源的使用情况。它可以从一个shared_ptr或另一个weak_ptr对象构造,它的构造和析构不会引起引用记数的增加或减少。

         在使用多线程过程中,遇到多个线程同时访问读写同一个对象时,一种方法是使用share_ptr加锁mutex操作,另一方法就是配合shared_ptr,使用weak_ptr。weak_ptr虽然没有重载*和->,但可以使用lock获得一个可用的shared_ptr对象,使对象自己能够生产shared_ptr来管理自己,本质上也是加锁。

        对于其助手类enable_shared_from_this的shared_from_this会返回this的shared_ptr,只需要让想被shared_ptr管理的类从它继承即可。

5、scoped_array与share_array

       scoped_array 与scoped_ptr源于相同的设计思想,故而用法非常相似,它只能在被声明的作用域内使用,不能拷贝、赋值。唯一不同的是scoped_array包装的是new[ ]产生的指针,并在析构时调用delete[ ],因为它管理的是动态数组,而不是单个动态对象。需要注意的是我们尽量避免使用 new[ ]操作符,它比new更可怕,是许多错误的来源。比如:

         int *p = new int[10];  delete p;

       这样的代码完全可以通过编译,无论是编译器还是程序员都很难区别出new[ ]和new分配的空间,而错误的运用delete将导致资源异常。在实际的应用中,在需要动态数组的情况下我们应该使用std::vector,它比scoped_array提供更多的灵活性,而且只付出了很小的代价。因此,除非对性能有非常苛刻的要求,或者编译器不支持标准库(比如某些嵌入式操作系统),否则不推荐使用scoped_array。

        share_array就像share_ptr和scoped_array的结合体,即具有share_ptr的优点,也具有scoped_array的缺点。shared_array能力有限,多数情况下它可以用shared_ptr<std::vector>或者std::vector<shared_ptr>来代替,这两个方案具有更好的安全性和更多的灵活性,而所付出的代价几乎可以忽略不计。


阅读全文
0 0
原创粉丝点击