Boost 中的智能指针

boost::shared_ptr

boost::shared_ptr 是一个很强大的智能指针封装,它通过一个指针和一个计数器来实现,当计数器的引用数为 0 时,自动析构对象,避免了由于种种原因可能导致的内存泄漏。

线程安全

shared_ptr 的计数器是安全(而且是无锁的),但是本身不是安全,它的成员变量就有两个。它的安全程度和内建类型(例如int,long)是一个级别上的。多个线程同时读写多个 shared_ptr 对象时可以的,哪怕它们都指向同一个对象。

boost 中给出的说明:

shared_ptr objects offer the same level of thread safety as built-in types. A shared_ptr instance can be "read " (accessed using only const operations) simultaneously by multiple threads. Different shared_ptr instances can be "written to " (accessed using mutable operations such as operator= or reset) simultaneosly by multiple threads (even when these instances are copies, and share the same reference count underneath.)

容器使用

shared_ptr 可以比较安全的放在容器中使用,因为它支持拷贝语义和比较操作,能比较好的支持容器。

如果在工程中计划使用 shared_ptr 来管理对象,一个简单的原则:总是使用一个具名智能指针变量去保存 new 的返回值,所有有 new 出现的地方,都是 shared_ptr<T> t(new T) 类似的代码,避免原生指针和智能指针的混用。

一个由临时变量而导致容易产生内存泄漏的陷阱:

void f(shared_ptr<int>, int);  
int g();  
void ok()  
{  
    shared_ptr<int> p(new int(2));  
    f(p, g());  
}  
void bad()  
{  
    f(shared_ptr<int>(new int(2)), g());  
}

当程序执行顺序为 new int —> g() —> shared_ptr 时,如果 g() 出错抛出异常,则可能导致内存泄漏。

boost::weak_ptr

boost::shared_ptr 很强大,但是也不是万能的,在这么一个场景下:对象 a 和对象 b,相互引用了对方的 share_ptr,在超出了 a 和 b 的作用域之时,因为 a 和 b 对象的 shared_ptr 计数为1,所以无法自动析构,将导致内存泄漏。

此时的解决方法:要么用原生的指针,手工释放;要么就使用 boost::weak_ptr。很显然,在一个使用了 boost::shared_ptr 的生产环境下,再使用原生的指针,是一件让人蛋疼的事情。

boost::weak_ptr 的声明可以简化如下:

namespace boost {

  template<class T> class weak_ptr {

    public:
      typedef T element_type;

      weak_ptr();

      template<class Y> weak_ptr(shared_ptr<Y> const & r);
      weak_ptr(weak_ptr const & r);
      template<class Y> weak_ptr(weak_ptr<Y> const & r);

      ~weak_ptr();

      weak_ptr & operator=(weak_ptr const & r);
      template<class Y> weak_ptr & operator=(weak_ptr<Y> const & r);
      template<class Y> weak_ptr & operator=(shared_ptr<Y> const & r);

      long use_count() const;
      bool expired() const;
      shared_ptr<T> lock() const;

      void reset();
      void swap(weak_ptr<T> & b);
  };

  template<class T, class U>
    bool operator<(weak_ptr<T> const & a, weak_ptr<U> const & b);

  template<class T>
    void swap(weak_ptr<T> & a, weak_ptr<T> & b);
}

可以看到,boost::weak_ptr 必须从一个 boost::share_ptr 或另一个 boost::weak_ptr 转换而来,这也说明,进行该对象的内存管理的是那个强引用的 boost::share_ptr。boost::weak_ptr 只是提供了对管理对象的一个访问手段。

boost::weak_ptr 除了对所管理对象的基本访问功能(通过 get() 函数)外,还有两个常用的功能函数: expired() 用于检测所管理的对象是否已经释放;lock() 用于获取所管理的对象的强引用指针。

boost 官方说明: “A weak_ptr can only be created from a shared_ptr”,不能在构造函数中创建自己的 weak_ptr。

shared_ptr 和 weak_ptr 是一个强引用和弱引用的关系:

一个强引用当被引用的对象活着的话,这个引用也存在(就是说,当至少有一个强引用,那么这个对象就不能被释放)。

相对而言,弱引用当引用的对象活着的时候不一定存在, 仅仅是当它存在的时候的一个引用。

弱引用并不修改该对象的引用计数,这意味这弱引用它并不对对象的内存进行管理,在功能上类似于普通指针. 一个比较大的区别是,弱引用能检测到所管理的对象是否已经被释放,从而避免访问非法内存。

boost::enable_shared_from_this

boost::enable_shared_from_this 也是一个很有用的类,它为了在类成员函数中能获得对象自身的 shared_ptr 而服务。

boost 给出的官方说明:

“It is used as a base class that allows a shared_ptr to the current object to be obtained from within a member function”.

具体一点,enable_shared_from_this<T> 作为class T 的基类,那么在T的成员函数中,可以通过 shared_from_this 函数来获取 shared_ptr<T> 和 const shared_ptr<T>。

源码实现如下:

#ifndef BOOST_SMART_PTR_ENABLE_SHARED_FROM_THIS_HPP_INCLUDED
#define BOOST_SMART_PTR_ENABLE_SHARED_FROM_THIS_HPP_INCLUDED

//
//  enable_shared_from_this.hpp
//
//  Copyright 2002, 2009 Peter Dimov
//
//  Distributed under the Boost Software License, Version 1.0.
//  See accompanying file LICENSE_1_0.txt or copy at
//  http://www.boost.org/LICENSE_1_0.txt
//
//  http://www.boost.org/libs/smart_ptr/enable_shared_from_this.html
//

#include <boost/smart_ptr/weak_ptr.hpp>
#include <boost/smart_ptr/shared_ptr.hpp>
#include <boost/assert.hpp>
#include <boost/config.hpp>

namespace boost
{

template<class T> class enable_shared_from_this
{
protected:

    enable_shared_from_this()
    {
    }

    enable_shared_from_this(enable_shared_from_this const &)
    {
    }

    enable_shared_from_this & operator=(enable_shared_from_this const &)
    {
        return *this;
    }

    ~enable_shared_from_this()
    {
    }

public:

    shared_ptr<T> shared_from_this()
    {
        shared_ptr<T> p( weak_this_ );
        BOOST_ASSERT( p.get() == this );
        return p;
    }

    shared_ptr<T const> shared_from_this() const
    {
        shared_ptr<T const> p( weak_this_ );
        BOOST_ASSERT( p.get() == this );
        return p;
    }

public: // actually private, but avoids compiler template friendship issues

    // Note: invoked automatically by shared_ptr; do not call
    template<class X, class Y> void _internal_accept_owner( shared_ptr<X> const * ppx, Y * py ) const
    {
        if( weak_this_.expired() )
        {
            weak_this_ = shared_ptr<T>( *ppx, py );
        }
    }

private:

    mutable weak_ptr<T> weak_this_;
};

} // namespace boost

#endif  // #ifndef BOOST_SMART_PTR_ENABLE_SHARED_FROM_THIS_HPP_INCLUDED

先看 shared_ptr 的构造函数,最常见的,boost::shared_ptr<T> ptr(new T) 这种的调用形式;

template<class Y>
explicit shared_ptr( Y * p ): px( p ), pn( p ) // Y must be complete
{
    boost::detail::sp_enable_shared_from_this( this, p, p );
}

sp_enable_shared_from_this 是个模板函数,实现如下:

template< class X, class Y, class T > inline void sp_enable_shared_from_this( boost::shared_ptr<X> const * ppx, Y const * py, boost::enable_shared_from_this< T > const * pe )
{
    if( pe != 0 )
    {
        pe->_internal_accept_owner( ppx, const_cast< Y* >( py ) );
    }
}

最后一个参数是 boost::enable_shared_from_this<T> const * 类型,class T 继承自 enable_shared_from_this<T>,此处是一个多态的实现。

换个角度来看,如果没有 enable_shared_from_this,我们要在类的成员函数中获得自身的 shared_ptr,需要先定义一个指向自身的 weak_ptr 的成员变量,然后通过这个 weak_ptr 来获得 shared_ptr。enable_shared_from_this 通过 C++ 的继承机制帮我们自动做好了这个事情。

从上面可以得到,enable_shared_from_this 的成员 weakthis 不是在 enable_shared_from_this 的构造函数中初始化的,而是通过 _internal_accept_owner 来赋初值的。而这个函数是 shared_ptr 在初始化的时候调用的。得出结论:不要在构造函数中使用 shared_from_this

另外需要注意的是,如果有多重继承的情况,而且不同的父类都继承了 boost::enable_shared_from_this,则会出现比较怪异的情况,具体的分析可以参考这里. 多重继承在 C++ 中原本就是不推荐的,所以应该在应用中尽量避免这种情况

参考文档