[记] C++智能指针

智能指针类型

  • C++98最早的智能指针auto_ptr已被废止。
  • C++11/14标准中的unique_ptr、shared_ptr和weak_ptr,源于boost中的scoped_ptr、shared_ptr和weak_ptr(boost中共有6种智能指针)

以boost 来说明

名称 头文件 说明
scoped_ptr 简单的单一对象的唯一所有权。不可拷贝。
scoped_array 简单的数组的唯一所有权。不可拷贝。
shared_ptr 在多个指针间共享的对象所有权。
shared_array 在多个指针间共享的数组所有权。
weak_ptr 一个属于 shared_ptr 的对象的无所有权的观察者。
intrusive_ptr 带有一个侵入式引用计数的对象的共享所有权。
  1. scoped_ptr(scoped_array)
    scoped_ptr包装了new在堆上的动态对象,能保证对象能在任何时候都被正确的删除。scoped_ptr的所有权更严格,不能转让。scoped_ptr,看名字,该智能指针只可以在本作用域内使用,不希望被转让。scoped_array和scoped_ptr的唯一不同是scoped_array管理数组对象。不建议使用scoped_array,可用vector替代。
template class scoped_ptr // noncopyable
{
private:

    T * px;//原始指针
    //禁止对智能指针的拷贝,保证被它管理的指针不被转让所有权。
    //如果一个类持有scoped_ptr成员,它也是禁止拷贝赋值的。
    scoped_ptr(scoped_ptr const &);//拷贝构造私有化
    scoped_ptr & operator=(scoped_ptr const &);//赋值操作私有化

    typedef scoped_ptr this_type;

    void operator==( scoped_ptr const& ) const;
    void operator!=( scoped_ptr const& ) const;

public:

    typedef T element_type;

    explicit scoped_ptr( T * p = 0 ); // never throws


    explicit scoped_ptr( std::auto_ptr p );

    ~scoped_ptr(); // never throws

    void reset(T * p = 0); // never throws,重置指针,一般不用
    //模拟原始指针操作,保存空指针时2操作未定义
    T & operator*() const; // never throws
    T * operator->() const; // never throws

    T * get() const;//获得原始指针

    void swap(scoped_ptr & b);//交换指针
};
  1. unique_ptr
    std::unique_ptr是C++11中新定义的智能指针,用于取代auto_ptr。unique_ptr不仅可以代理new创建的单个对象,也可以代理new[]创建的数组对象,就是说它结合了scoped_ptr和scoped_array两者的能力。
    unique_ptr的基本能力跟scoped_ptr一样,同样可以在作用域内管理指针,也不允许拷贝和赋值。

  2. shared_ptr
    shared_ptr是最像指针的智能指针。shared_ptr和scoped_ptr一样包装了new在堆上的动态对象,也不可以管理new[]产生的数组指针,也没有指针算术操作。但是shared_ptr实现了引用计数,可以自由拷贝赋值,在任意地方共享它,当没有代码使用它(引用计数为0)时,它才删除被包装的动态对象。shared_ptr可以被放在标准容器中,STL容器存储指针的标准做法。

工厂函数make_shared
shared_ptr消除了显示调用delete,但是没有消除显示调用new,boost提供一个工厂函数make_shared来消除显式new调用。例子:

void fun()
{
    auto sp = boost::make_shared("make_shared");//创建string共享指针
    auto spv = boost::make_shared< vector >(10,2);//vector共享指针
}
  1. weak_ptr
    weak_ptr是为了配合shared_ptr而引入的,它没有普通指针的行为,没重载*和->。它最大的作用是协助shared_ptr工作,像旁观者一样观测资源的使用情况。weak_ptrk可以从一个shared_ptr或者一个weak_ptr对象构造,获得对象资源观测权。但是weak_ptr并没有共享资源,它的构造不会引起引用计数的增加,也不会让引用计数减少,它只是静静的观察者。
template class weak_ptr
{
private:
    typedef weak_ptr this_type;

public:
    weak_ptr();

    ~weak_ptr();

    weak_ptr( weak_ptr const & r );

    weak_ptr & operator=( weak_ptr const & r );

    weak_ptr( weak_ptr const & r );

    shared_ptr lock() const;//获取shared_ptr,把弱关系转为强关系,操作资源

    long use_count() const;//引用计数

    bool expired() const;//是否失效指针

    void reset();//重置指针

    void swap(this_type & other);

    element_type * px;            // contained pointer
    boost::detail::weak_count pn; // reference counter

};  // weak_ptr

template inline bool operator<(weak_ptr const & a, weak_ptr const & b);
template void swap(weak_ptr & a, weak_ptr & b);

举例:

class self_shared: public enable_shared_from_this
{
public:
  int n;
  void fun();
};

int main()
{
    auto sp = make_shared(123);
    sp->fun();

    auto p = sp->shared_from_this();
    p->fun();

    //不能对普通对象使用shared_from_this
    self_shared s;
    auto ps = s->shared_from_this();//错误,shared_ptr会在析构时试图删除一个栈对象

    return 0;
}

打破循环引用:
有时候代码会出现循环引用,这时候shared_ptr的引用计数就会失效,导致不能正确释放资源。例如:

class node
{
    public:
    ~node(){cout<<"deleted.\n";}
    typedef shared_ptr ptr_type;
    ptr_type next;
};

int main()
{
    auto p1 = make_shared();
    auto p2 = make_shared();

    p1->next = p2;//形成循环链表
    p2->next = p1;

    cout<use_count()<use_count()<

两个node对象都互相持有对方的引用,每个shared_ptr对象的引用计数都是2,因此在析构时不能减为0,不会调用删除操作,导致内存泄漏。这个时候我们可以使用weak_ptr,因为它不会增加引用计数,这强引用变为弱引用,在可能循环的地方打破循环,真正需要shared_ptr的时候调用weak_ptr的lock()函数。

//修改以上
    typedef weak_ptr ptr_type;

    p1->next = p2;//形成循环链表
    p2->next = p1;//weak_ptr所以正常

    if (!p1->next->expired())//检查弱引用是否有效
    {
        auto p3 = p1->next->lock();//获得强引用
    }

    //退出后,shared_ptr都正确析构

Tips

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

参考

  • https://www.boost.org/doc/libs/1_63_0/libs/smart_ptr/shared_ptr.htm
  • http://zh.highscore.de/cpp/boost/smartpointers.html
  • https://www.cnblogs.com/dengchj/p/9188504.html

你可能感兴趣的:([记] C++智能指针)