Boost---boost.smart_ptr库中提供的智能指针

Boost提供了6种智能指针,包括scoped_ptr,scoped_array,shared_ptr,shared_array,weak_ptr,instrusive_ptr,从各个方面来增强std::auto_ptr,而且是异常安全的。库中的两个类——shared_ptr和weak_ptr已经被收入到C++新标准的TR1库中。

这些智能指针都位于名字空间boost,为了使用smart_ptr组建,需要包含头文件

#include<boost/smart_ptr.hpp>
using namespace boost;

1.scoped_ptr

scoped_ptr是一个很类似auto_ptr的智能指针,它包装了new操作符在堆上分配的动态对象,能够保证动态创建的对象在任何时候都可以被正确的删除。但scoped_ptr的所有权更加严格,不能转让,一旦scoped_ptr获取了对象的管理权,你就无法再从它那里获取回来。
scoped_ptr拥有一个很好的名字,它向代码的阅读者传递了明确的信息:这个智能指针只能再本作用域内使用,不希望被转让。
1.1用法
scoped_ptr的用法很简单:再原本使用指针变量接受new表达式结果的地方改用scoped_ptr对象,然后去掉那些多余的try/catch和delete操作就可以了。像这样:

scoped_ptr<string> sp(new string("text"));

scoped_ptr是一种“智能指针”,因此其行为与普通智能指针基本相同,可以使用非常熟悉的*和->操作符:

cout<<*sp<<endl;//取字符串的内容
cout<<sp->size()<<endl;//取字符串的长度

但记住:不再需要delete操作,scoped_ptr会自动的帮助我们释放资源,如果我们对scoped_ptr执行delete会得到一个编译错误,因为scoped_ptr是一个行为类似指针的对象,而不是指针,对一个对象应用delete是不允许的。
1.2与auto_ptr的区别:
scoped_ptr的用法与auto_ptr几乎一样,大多数情况下它可以与auto_ptr相互替换,它也可以从一个auto_ptr获得指针的管理权(同时auto_ptr失去管理权)。
scoped_ptr也具有auto_ptr同样的缺陷——不能用作容器的元素,但原因不同:auto_ptr是因为它的转移语义,而scoped_ptr则是因为不支持拷贝和赋值,不符合容器对元素类型的要求。
scoped_ptr与auto_ptr的根本性区别在于指针的所有权。auto_ptr特意被设计为指针的所有权是可转移的,可以在函数之间传递,同一时刻只能有一个auto_ptr管理指针。而scoped_ptr把拷贝构造函数和赋值函数都声明为私有的,拒绝了指针所有权的转让——除了scoped_ptr自己,其他任何人都无权访问被管理的指针,从而保证了指针的绝对安全。

2.scoped_array:

scoped_array很像scoped_ptr,它包装了new[]操作符(不是单纯的new)在堆上分配的动态数组,为动态数组提供了一个代理,保证可以正确地释放内存。
scoped_array弥补了标准库中没有指向数组的智能指针的缺憾。
scoped_array的接口和功能几乎是与scoped_ptr是相同的(甚至还要少一些),主要特点如下:
- 构造函数接受的指针p必须是new[]的结果,而不能是new表达式的结果;
- 没有*、->操作符重载,因为scoped_array持有的不是一个普通指针;
- 析构函数使用delete[]释放资源,而不是delete;
- 提供operator[]操作符重载,可以像普通数组一样用下表访问元素;
- 没有begin()、end()等类似容器的迭代器操作函数。
2.1用法:
scoped_array与scoped_ptr源于同样的设计思想,故而用法非常相似,它只能在被声明的作用域内使用,不能拷贝、赋值。唯一不同的是scoped_array包装的是new[]产生的指针,并在析构时调用delete[],因为它管理的是动态数组,而不是单个动态对象。

通常scoped_array的创建方式是这样的:

scoped_array sa(new int[100]);//包装动态数组

scoped_array重载了operator[],因此它用起来就像是一个普通的数组,但因为它不提供指针运算,所以不能用“数组首地址+N”的方式访问数组元素:

sa[0]=10;//正确用法,使用operator[]
*(sa+1)=20;//错误用法,不能通过编译

在使用重载的operator[]时要小心,scoped_array不提供数组索引的范围检查,如果使用了超过动态数组大小的索引或者时负数索引将引发未定义行为。
2.2使用建议:
scoped_array没有给程序增加额外的负担,用起来很方便轻巧。它的速度与原始数组同样快,很适合那些习惯于使用new操作符在堆上分配内存的程序员。但scoped_array的功能很有限,不能动态增长,也没有迭代器支持,不能搭配STL算法,仅有一个纯粹的“裸”数组接口。而且我们应当尽量避免使用new[]操作符,它比new更可怕,时许多错误的来源,因为

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

这样的代码完全可以通过编译,无论是编译器还是程序员都很难区别出new和new[]分配的空间,而错误的运用delete将导致资源异常。

在需要动态数组的情况下我们应该使用std::vector,它比scoped_array提供了更多的灵活性,而只付出了很小的代价。

除非对性能有非常苛刻的要求,或者编译器不支持标准库,否则不推荐使用scoped_array,它只是为了与老式C风格代码兼容而使用的类,它的出现往往意味着你的代码中存在着隐患。

3.shared_ptr:

shared_ptr是一个最想指针的智能指针,是boost.smart_ptr库中最有价值,最重要的组成部分,也是最有用的,Boost库中的许多组建——甚至还包括其他一些领域的智能指针都使用了shared_ptr。

shared_ptr和scoped_ptr一样包装了new操作符在堆上分配的动态对象,但它实现的是引用计数型的智能指针,可以被自由的拷贝和赋值,在任意的地方共享它,当没有代码使用(引用计数为0)它时才删除被包装的动态分配的对象。shared_ptr也可以安全的放到标准容器中,并弥补了auto_ptr因为转移语义而不能把指针作为STL容器元素的缺陷。

3.1用法:
shared_ptr的智能使其行为最接近原始指针,因此它比auto_ptr和scoped_ptr的应用范围更广。几乎是100%可以在任何new出现的地方接受new的动态分配结果,然后被任意使用,从而完全消灭delete的使用和内存泄漏,而它的用法与auto_ptr和scoped_ptr一样简单。

shared_ptr也提供基本的线程安全保证,一个shared_ptr可以被多个线程安全读取,但其他的访问形式结果是未定义的。
应用于标准容器:
有两种方式可以将shared_ptr应用于标准容器(或者容器适配器等其他容器)。
1)、将容器作为shared_ptr管理的对象,如shared_ptr<list<T> >,使容器可以被安全的共享,用法与普通shared_ptr没有区别;
2)、将shared_ptr作为容器的元素,如vector<shared_ptr<T> >,因为shared_ptr支持拷贝语义和比较操作,复合标准容器对元素的要求,所以可以实现在容器中安全的容纳元素的指针而不是拷贝。
标准容器不能容纳auto_ptr,这时C++标准特别规定的。标准容器也不能容纳scoped_ptr,因为scoped_ptr不能拷贝和赋值。标准容器可以容纳原始指针,但这就丧失了容器的许多好处,因为标准容器无法自动管理类型为指针的元素,必须编写额外的大量代码来保证指针最终被正确删除,这通常很麻烦很难实现。

存储shared_ptr的容器与存储原始指针的容器功能几乎一样,但shared_ptr为程序员做了指针的管理工作,可以任意使用shared_ptr而不用担心资源泄露。

4.shared_array:

shared_array类似shared_ptr,它包装了new[]操作符在堆上分配的动态数组,同样使用引用计数机制为动态数组提供了一个代理,可以在程序的生命周期里长期存在,直到没有任何引用后再释放内存。
shared_array的接口与功能几乎是与shared_ptr相同的,主要区别如下:
- 构造函数接受的指针p必须是new[]的结果,而不能是new表达式的结果;
- 提供operator[]操作符重载,可以像普通数组一样用下表访问元素;
- 没有*、->操作符重载,因为shared_array持有的不是一个普通指针;
- 析构函数使用delete[]释放资源,而不是delete。
4.1 用法:

shared_array就像是shared_ptr和scoped_array的结合体——即具有shared_ptr的优点也有scoped_array的缺点。

#include<boost/smart_ptr.hpp>
using namespace boost;
int main()
{
    int *p = new int[100];
    shared_array<int> sa(p);//shared_array代理动态数组
    shared_array<int> sa2=sa;//共享数组,引用计数增加

    sa[0]=10;//可以使用operator[]访问元素
    assert(sa2[0]==10);
}//离开作用于,自动删除动态数组

同样的,在使用shared_array重载的operator[]时要小心,shared_array不提供数组索引的范围检查,如果使用了超过动态数组大小的索引或者是负数索引将引发未定义行为。

shared_array能力有限,多数情况下可用shared_ptr<std::vector> 或者std::vector<shared_ptr>来代替,这两个方案具有更好的安全性和更多的灵活性,而所付出的代价几乎可以忽略不计。

5.weak_ptr:

weak_ptr是为配合shared_ptr而引入的一种智能指针,它更像是shared_ptr的一个助手而不是智能指针,因为它不具有普通指针的行为,没有重载operator*和->,它的最大作用在于协助shared_ptr工作,像旁观者那样观测资源的使用情况。
5.1用法:

web_ptr被设计为与shared_ptr共同工作,可以从一个shared_ptr或者另一个weak_ptr对象构造,获得资源的观测权。但weak_ptr没有共享资源,它的构造不会引起指针引用计数的增加。同样在weak_ptr析构时也不会导致引用计数减少,它只是一个静静的旁观者。

weak_ptr没有重载operator*和->,这是特意的,因为它不共享指针,不能操作资源,这正是它“弱”的原因。但它可以使用一个非常重要的成员函数lock()从被观测的shared_ptr获得一个可用的shared_ptr对象,从而操作资源。但当expired()==true(expired()函数的功能等价于use_count()==0,表示被观测的资源也就是shared_ptr管理的资源已经不存在。use_count()函数用于观测资源的引用计数)的时候,lock()函数将返回一个存储空指针的shared_ptr。
获得this的shared_ptr:

webak_ptr的一个重要用途是获得this指针的shared_ptr,使对象自己能够生产shared_ptr管理自己;对象使用weak_ptr观测this指针,这并不影响引用计数,在需要的时候就调用lock()函数,返回一个符合要求的shared_ptr供外界使用。

这个解决方案被实现为一个惯用法,在头文件<boost/enable_shared_from_this.hpp>定义了一个助手类enable_shared_from_this<T>,它的声明摘要如下:

template<class T>
    class enable_shared_from_this
    {
        public:
            shared_ptr<T> shared_from_this();
    }

使用的时候只需要让想被shared_ptr管理的类从它继承即可,成员函数shared_from_this()会返回this的shared_ptr。例如:

#include<boost/enable_shared_from_this.hpp>     
#include<boost/make_shared.hpp>
class self_shared:public enable_shared_from_this<self_shared>//一个需要用shared_ptr自我管理的类
{
    public:
        self_shared(int n):x(n){}
        int x;
        void print(){
        cout<<"self_shared:"<<x<<endl;
        }
};

int main()
{
    shared_ptr<self_shared> sp = make_shared<self_shared>(314);
    sp->print();

    shared_ptr<self_shared> p = sp->shared_from_this();
    p->x=1000;
    p->print();
}

需要注意的是千万不能从一个普通对象(非shared_ptr)使用shared_from_this()获得shared_ptr,例如:

self_shared ss;
shared_ptr<self_shared> p = ss.shared_from_this();//错误

这样虽然语法上正确,编译也无问题,但在运行时会导致shared_ptr析构时企图删除一个栈上分配的对象,发生未定义行为。

6.intrusive_ptr:

intrusive_ptr是一个侵入式的引用计数型指针,它可以用于以下两种情形:

  • 对内存占用的要求非常严格,要求必须与原始指针一样;
  • 现存代码已经有了引用计数机制管理的对象。
    Boost库不推荐使用intrusive_ptr,因为shared_ptr已经非常强大且灵活,工作的足够好,可以满足绝大部分的需要。
发布了46 篇原创文章 · 获赞 13 · 访问量 6万+

猜你喜欢

转载自blog.csdn.net/luliuliu1234/article/details/80471258