Boost中的智能指针详细总结

1、前言

学习过C/C++的同学都知道,有一个非常方便又特别让人烦的数据类型,那就是指针,不仅操作麻烦,还有各种“级别”,一级指针、二级指针、n级别指针…很多人从入门到放弃C/C++,很大一个原因是因为搞不清楚指针、以及指针与其他对象之间千丝万缕的关系。而在实际的开发过程中,经常会出现因为未释放申请的对象而导致内存溢出、程序奔溃。这里就包括指针对象的释放,那如果有一种指针,能申请对象后“自动”释放,是不是很爽?本文将介绍Boost中提到的各种智能指针。

C++中出现智能是在1998年修订的第一版C++标准中: std::auto_ptr 。 与下面boost中的其他智能指针相比,它就像是个普通的指针: 通过地址来访问一个动态分配的对象。 它会在析构时调用 delete 操作符来自动释放所包含的对象。 前提是在初始化的时候,传给它一个由 new 操作符返回的对象地址。这就明显减少了程序员手动delete的繁琐操作。

另一方便,程序中频繁出现的“异常”,也可以导致未正确释放内存,而出现内存泄漏。而智能指针,可以在析构函数中准确释放内存,避免出现内存泄漏。这也是智能指针的优点之一。 Boost C++ 库 Smart Pointers 提供了许多可以用在各种场合的智能指针。

2、你知道RAII吗?

习语 RAII(Resource Acquisition Is Initialization) :资源申请即初始化。智能指针是该习语重要的实现之一。 智能指针确保在任何情况下,动态分配的内存都能得到正确释放,从而将开发人员从这项任务中解放出来。 这包括程序因为异常而中断,原本用于释放内存的代码被跳过的场景。 用一个动态分配的对象的地址来初始化智能指针,在析构的时候释放内存,就确保了这一点。 因为析构函数总是会被执行的,这样所包含的内存也将总是会被释放。

无论何时,一定得有第二条指令来释放之前另一条指令所分配的资源时,RAII 都是适用的。 许多的 C++ 应用程序都需要动态管理内存,因而智能指针是一种很重要的 RAII 类型。 不过 RAII 本身是适用于许多其它场景的。

基于上述描述,如下代码片段,模拟智能指针实现。

// Smart_ptr.hpp
#include <iostream>

using namespace std;

template<class T>
class Smart_ptr
{

public:
	Smart_ptr(T *t)
	{
		cout << "Smart_ptr 构造函数" << endl;
		this->t = t;
	}

	~Smart_ptr()
	{
		if (this->t != NULL)
		{
			cout << "Smart_ptr 西沟函数" << endl;
			delete this->t;
		}
	}

	T* &operator->()
	{
		return this->t;
	}

private:
	T *t;
};


构造Person测试类

#include <iostream>
#include <string>
using namespace std;

class Person
{
public:
	Person(std::string name, int age) :name(name), age(age)
	{
		cout << "Person 的构造函数" << endl;
	}

	Person()
	{
		cout << "Person 的默认构造" << endl;
	}

	void operator=(const Person& p)
	{
		this->age = p.age;
		this->name = p.name;
	}

	~Person()
	{
		cout << "Person 的析构函数" << endl;
	}

	void Show()
	{
		cout << this->name << "小朋友今年" << this->age << "岁了" << endl;
	}

private:
	std::string name;
	int age;
};

测试代码

#include"Smart_ptr.hpp"
#include"Person.hpp"

nt main()
{	
	Smart_ptr<Person> p = Smart_ptr<Person>(new Person("小花", 22));
	p->Show();
	return 0;
}

运行结果
在这里插入图片描述
从测试代码中看出,我new了Person对象,在程序结束时并没有delete,但是却打印出了调用析构函数的日志【不信你可以断点调试这个程序,O(∩_∩)O哈哈~】。这就说明智能指针已经起作用了,帮你省掉了delete的步骤。是不是很开心^-^。

上述只是简单地引入了一下智能指针的概念,以及描述了智能指针的作用,下面将着重介绍boost库中提供的强大的智能指针。

3、作用域指针

3.1、概述

作用域指针类名为: boost::scoped_ptr。 一个作用域指针独占一个动态分配的对象。它的定义在 boost/scoped_ptr.hpp 中。 不像 std::auto_ptr,一个作用域指针不能传递它所包含的对象到另一个作用域指针,也就是说无法通过拷贝构造传参。 一旦用一个地址来初始化,这个动态分配的对象将在析构阶段释放。

一个作用域指针只独占一个内存地址,所以 boost::scoped_ptr 的实现就要比 std::auto_ptr 简单。 在不需要拷贝构造传递的时候应该优先使用 boost::scoped_ptr 。

3.2、如何使用scoped_ptr

下面代码片段,演示如何调用boost::scoped_ptr。其中Person对象贯穿全篇,定义在本文开头处。


int main()
{
	boost::scoped_ptr<Person> p(new Person("小红", 12));
	p->Show();   // 等价于 p.get()->Show(); 
	 
	//boost::scoped_ptr<Person> p1(p);;  // 无法使用scoped_ptr拷贝构造定义初始化另一个对象
	return 0;
}

执行结果
在这里插入图片描述

一经初始化,智能指针 boost::scoped_ptr 所包含的对象,可以通过类似于普通指针的接口来访问。 这是因为重载了相关的操作符 operator()*operator->()operator bool() 。 此外,还有 get()reset() 方法。 前者返回所含对象的地址,后者用一个新的对象来重新初始化智能指针。 在这种情况下,新创建的对象赋值之前会先自动释放所包含的对象。

boost::scoped_ptr 的析构函数中使用 delete 操作符来释放所包含的对象。 这对 boost::scoped_ptr 所包含的类型加上了一条重要的限制。 即boost::scoped_ptr 不能用动态分配的数组来做初始化,因为这需要调用 delete[] 来释放。 在这种情况下,可以使用下面将要介绍的 boost:scoped_array 类。

3.3、scoped_ptr源码分析

如下代码片段是截取的boost::scoped_ptr源码核心部分,相关解释见注释。

// scoped_ptr.hpp
#include <boost/config.hpp>
#include <boost/assert.hpp>
#include <boost/checked_delete.hpp>
#include <boost/smart_ptr/detail/sp_nullptr_t.hpp>
#include <boost/smart_ptr/detail/sp_disable_deprecated.hpp>
#include <boost/smart_ptr/detail/sp_noexcept.hpp>
#include <boost/detail/workaround.hpp>

#ifndef BOOST_NO_AUTO_PTR
# include <memory>          // for std::auto_ptr
#endif


namespace boost
{
//  scoped_ptr mimics a built-in pointer except that it guarantees deletion
//  of the object pointed to, either on destruction of the scoped_ptr or via
//  an explicit reset(). scoped_ptr is a simple solution for simple needs;
//  use shared_ptr or std::auto_ptr if your needs are more complex.

template<class T> class scoped_ptr // noncopyable
{
private:

    T * px;      // 内部私有指针变量, 用于存储初始化的对象。

    scoped_ptr(scoped_ptr const &);    //拷贝构造私有化,不允许传递对象到其他指针对象
    scoped_ptr & operator=(scoped_ptr const &);  // 重载=号运算符私有化,同样是不允许传递对象到其他指针

    typedef scoped_ptr<T> 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 ) BOOST_SP_NOEXCEPT : px( p )
    {
#if defined(BOOST_SP_ENABLE_DEBUG_HOOKS)
        boost::sp_scalar_constructor_hook( px );
#endif
    }

#ifndef BOOST_NO_AUTO_PTR

    explicit scoped_ptr( std::auto_ptr<T> p ) BOOST_SP_NOEXCEPT : px( p.release() )
    {
#if defined(BOOST_SP_ENABLE_DEBUG_HOOKS)
        boost::sp_scalar_constructor_hook( px );
#endif
    }

#endif
	// scoped_ptr 析构函数,可以看出调用了delete释放对象内存
    ~scoped_ptr() BOOST_SP_NOEXCEPT
    {
#if defined(BOOST_SP_ENABLE_DEBUG_HOOKS)
        boost::sp_scalar_destructor_hook( px );
#endif
        boost::checked_delete( px );
    }
	// 重置对象为默认值,类似p=NULL
    void reset(T * p = 0) BOOST_SP_NOEXCEPT_WITH_ASSERT
    {
        BOOST_ASSERT( p == 0 || p != px ); // catch self-reset errors
        this_type(p).swap(*this);
    }

    T & operator*() const BOOST_SP_NOEXCEPT_WITH_ASSERT
    {
        BOOST_ASSERT( px != 0 );
        return *px;
    }

    T * operator->() const BOOST_SP_NOEXCEPT_WITH_ASSERT
    {
        BOOST_ASSERT( px != 0 );
        return px;
    }

    T * get() const BOOST_SP_NOEXCEPT
    {
        return px;
    }

// implicit conversion to "bool"
#include <boost/smart_ptr/detail/operator_bool.hpp>

    void swap(scoped_ptr & b) BOOST_SP_NOEXCEPT
    {
        T * tmp = b.px;
        b.px = px;
        px = tmp;
    }
};

} // namespace boost

4、作用域数组

4.1、概述

作用域数组的使用方式与作用域指针相似。 不同点在于,作用域数组的析构函数使用 delete[] 操作符来释放所包含的对象。 因为该操作符只能用于数组对象,所以作用域数组必须通过动态分配的数组来初始化。作用域数组类名为 boost::scoped_array。下面章节介绍如何使用作用域数组。

4.2、调用实例
#include<boost/scoped_array.hpp>

int main()
{
	boost::scoped_array<int> i(new int[2]);

	*i.get() = 1;
	i[1] = 2;
	i[0] = 1;
	 
	cout << i[0] << endl;
	cout << i[1] << endl;
	
	return 0;
}

运行结果
在这里插入图片描述

4.3、源码分析

boost::acoped_array 核心源码如下代码段:

#include <boost/config.hpp>
#include <boost/assert.hpp>
#include <boost/checked_delete.hpp>
#include <boost/smart_ptr/detail/sp_nullptr_t.hpp>
#include <boost/smart_ptr/detail/sp_noexcept.hpp>

#include <boost/detail/workaround.hpp>

#include <cstddef>            // for std::ptrdiff_t

namespace boost
{
//  scoped_array extends scoped_ptr to arrays. Deletion of the array pointed to
//  is guaranteed, either on destruction of the scoped_array or via an explicit
//  reset(). Use shared_array or std::vector if your needs are more complex.

template<class T> class scoped_array // noncopyable
{
private:

    T * px;    //私有变量,存储初始化对象

    scoped_array(scoped_array const &);   // 私有拷贝构造,和作用域指针类似,不允许拷贝构造
    scoped_array & operator=(scoped_array const &);  // 私有化=运算符

    typedef scoped_array<T> this_type;

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

public:

    typedef T element_type;

    explicit scoped_array( T * p = 0 ) BOOST_SP_NOEXCEPT : px( p )
    {
#if defined(BOOST_SP_ENABLE_DEBUG_HOOKS)
        boost::sp_array_constructor_hook( px );
#endif
    }

    ~scoped_array() BOOST_SP_NOEXCEPT
    {
#if defined(BOOST_SP_ENABLE_DEBUG_HOOKS)
        boost::sp_array_destructor_hook( px );
#endif
        boost::checked_array_delete( px );
    }

    void reset(T * p = 0) BOOST_SP_NOEXCEPT_WITH_ASSERT
    {
        BOOST_ASSERT( p == 0 || p != px ); // catch self-reset errors
        this_type(p).swap(*this);
    }

    T & operator[](std::ptrdiff_t i) const BOOST_SP_NOEXCEPT_WITH_ASSERT
    {
        BOOST_ASSERT( px != 0 );
        BOOST_ASSERT( i >= 0 );
        return px[i];
    }

    T * get() const BOOST_SP_NOEXCEPT
    {
        return px;
    }

// implicit conversion to "bool"
#include <boost/smart_ptr/detail/operator_bool.hpp>

    void swap(scoped_array & b) BOOST_SP_NOEXCEPT
    {
        T * tmp = b.px;
        b.px = px;
        px = tmp;
    }
};

5、共享指针

5.1、概述

共享指针是使用率最高的智能指针。 如果开发环境支持的话,可以使用 memory 中定义的 std::shared_ptr。 在 Boost C++ 库里,这个智能指针命名为 boost::shared_ptr

boost::shared_ptr 类似 boost::scoped_ptr。 不同之处在于 boost::shared_ptr 可以不独占一个对象。 它可以和其他 boost::shared_ptr 类型的智能指针共享所有权。 在这种情况下,引用对象的最后一个智能指针销毁后,对象才会被释放。

5.2、使用示例

boost::shared_ptr开放拷贝构造,任何一个共享指针都可以被复制,这跟 boost::scoped_ptr 是不同的。如下示例代码

#include<boost/shared_ptr.hpp>
#include<vector>

int main()
{
	// 在标准容器中安全的使用动态分配的对象
	std::vector<boost::shared_ptr<int>> v;
	v.push_back(boost::shared_ptr<int>(new int(3)));
	v.push_back(boost::shared_ptr<int>(new int(2)));

	// 使用拷贝构造
	boost::shared_ptr<int> p(new int(2));
	boost::shared_ptr<int> p1(p);
	p.reset(new int);
	
	// 创建自定义对象共享指针
	boost::shared_ptr<Person> pson;

	pson->Show();
}
5.3、源码分析

下面代码片段截取部分核心源码,注释的方式讲解共享指针核心功能。


template<class T> class shared_ptr
{
public:
	// 构造函数
    template<class Y>
    explicit shared_ptr( Y * p ): px( p ), pn() // Y must be complete
    {
        boost::detail::sp_pointer_construct( this, p, pn );
    }

 	// 拷贝构造函数
    shared_ptr( shared_ptr const & r ) BOOST_SP_NOEXCEPT : px( r.px ), pn( r.pn )
    {
    }

	// 使用weak_ptr构造
    template<class Y>
    explicit shared_ptr( weak_ptr<Y> const & r ): pn( r.pn ) // may throw
    {
        boost::detail::sp_assert_convertible< Y, T >();

        // it is now safe to copy r.px, as pn(r.pn) did not throw
        px = r.px;
    }


    // 拷贝构造 
    template< class Y >
    shared_ptr( shared_ptr<Y> const & r, element_type * p ) BOOST_SP_NOEXCEPT : px( p ), pn( r.pn )
    {
    }

	// 使用auto_ptr 构造
    template<class Y>
    explicit shared_ptr( std::auto_ptr<Y> & r ): px(r.get()), pn()
    {
        boost::detail::sp_assert_convertible< Y, T >();

        Y * tmp = r.get();
        pn = boost::detail::shared_count( r );

        boost::detail::sp_deleter_construct( this, tmp );
    }
	
	// 使用unique_ptr构造
    template< class Y, class D >
    shared_ptr( std::unique_ptr< Y, D > && r ): px( r.get() ), pn()
    {
        boost::detail::sp_assert_convertible< Y, T >();

        typename std::unique_ptr< Y, D >::pointer tmp = r.get();

        if( tmp != 0 )
        {
            pn = boost::detail::shared_count( r );
            boost::detail::sp_deleter_construct( this, tmp );
        }
    } 
	
	//重载=运算符
    shared_ptr & operator=( shared_ptr const & r ) BOOST_SP_NOEXCEPT
    {
        this_type(r).swap(*this);
        return *this;
    }
 
	//重载=运算符 ,可以实现a=b=c这种赋值操作
    template<class Y, class D>
    shared_ptr & operator=( std::unique_ptr<Y, D> && r )
    {
        this_type( static_cast< std::unique_ptr<Y, D> && >( r ) ).swap(*this);
        return *this; 
	}
	
    template<class Y, class D>
    shared_ptr & operator=( boost::movelib::unique_ptr<Y, D> r )
    {
        // this_type( static_cast< unique_ptr<Y, D> && >( r ) ).swap( *this );

        boost::detail::sp_assert_convertible< Y, T >();

        typename boost::movelib::unique_ptr< Y, D >::pointer p = r.get();

        shared_ptr tmp;

        if( p != 0 )
        {
            tmp.px = p;
            tmp.pn = boost::detail::shared_count( r );

            boost::detail::sp_deleter_construct( &tmp, p );
        }

        tmp.swap( *this );

        return *this;
    }

	//重置对象
    void reset() BOOST_SP_NOEXCEPT
    {
        this_type().swap(*this);
    }

   
	//重载*运算符
    typename boost::detail::sp_dereference< T >::type operator* () const BOOST_SP_NOEXCEPT_WITH_ASSERT
    {
        BOOST_ASSERT( px != 0 );
        return *px;
    }
    //重载->运算符
    typename boost::detail::sp_member_access< T >::type operator-> () const BOOST_SP_NOEXCEPT_WITH_ASSERT
    {
        BOOST_ASSERT( px != 0 );
        return px;
    }
    //重载[]运算符
    typename boost::detail::sp_array_access< T >::type operator[] ( std::ptrdiff_t i ) const BOOST_SP_NOEXCEPT_WITH_ASSERT
    {
        BOOST_ASSERT( px != 0 );
        BOOST_ASSERT( i >= 0 && ( i < boost::detail::sp_extent< T >::value || boost::detail::sp_extent< T >::value == 0 ) );

        return static_cast< typename boost::detail::sp_array_access< T >::type >( px[ i ] );
    }
	// 获取对象实例
    element_type * get() const BOOST_SP_NOEXCEPT
    {
        return px;
    }

	// 只有一个资源,返回true,如果有两个或两个以上资源,则返回false
    bool unique() const BOOST_SP_NOEXCEPT
    {
        return pn.unique();
    }
	// 返回资源数
    long use_count() const BOOST_SP_NOEXCEPT
    {
        return pn.use_count();
    }
	// 交换资源
    void swap( shared_ptr & other ) BOOST_SP_NOEXCEPT
    {
        std::swap(px, other.px);
        pn.swap(other.pn);
    }

private:

    template<class Y> friend class shared_ptr;
    template<class Y> friend class weak_ptr;


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

};  // shared_ptr

从源码可以看出,shared_ptr开放了拷贝构造、等号运算符等功能,使用上比前两者更强大,同时支持多种智能指针构造,比如 weak_ptrauto_ptr 等。同时重载了指针相关的运算符,在用户体验上做的更加完美。

6、共享数组

6.1、概述

共享数组的行为类似于共享指针。 关键不同在于共享数组在析构时,默认使用 delete[] 操作符来释放所含的对象。 因为这个操作符只能用于数组对象,共享数组必须通过动态分配的数组的地址来初始化。

共享数组对应的类型是 boost::shared_array

6.2、使用示例

#include <boost/shared_array.hpp>
int main()
{
	boost::shared_array<int> arr(new int[3]);

	arr[0] = 1;
	arr[1] = 2;
	arr[2] = 3;

	boost::shared_array<int> arr1(arr);

	cout << arr1[0] << endl;
	cout << arr1[1] << endl;
	cout << arr1[2] << endl;

	cout << "usr_count is :" << arr1.use_count() << endl;

	if (arr1.unique())
	{
		cout << "unique" << endl;
	}
	else
	{
		cout << "not unique" << endl;
	}

	return 0;
}

运行结果
在这里插入图片描述
可以看出,boost::shared_arrayboost::shared_ptr 神似,只是前者需要数组初始化,因为析构的时候使用delete[]

6.3、源码分析
template<class T> class shared_array
{

public:

    typedef T element_type;

	// 默认构造函数
    shared_array() BOOST_SP_NOEXCEPT : px( 0 ), pn()
    {
    }
 
	// 有参构造函数 
    template<class Y>
    explicit shared_array( Y * p ): px( p ), pn( p, checked_array_deleter<Y>() )
    {
        boost::detail::sp_assert_convertible< Y[], T[] >();
    }


	// 拷贝构造函数
    template< class Y >
    shared_array( shared_array<Y> const & r, element_type * p ) BOOST_SP_NOEXCEPT : px( p ), pn( r.pn )
    {
    }
 
	// 重载=
    shared_array & operator=( shared_array const & r ) BOOST_SP_NOEXCEPT
    {
        this_type( r ).swap( *this );
        return *this;
    } 
	// 重置指针对象
    void reset() BOOST_SP_NOEXCEPT
    {
        this_type().swap( *this );
    }
 
	//重载[]
    T & operator[] (std::ptrdiff_t i) const BOOST_SP_NOEXCEPT_WITH_ASSERT
    {
        BOOST_ASSERT(px != 0);
        BOOST_ASSERT(i >= 0);
        return px[i];
    }
    // 获取指针本体
    T * get() const BOOST_SP_NOEXCEPT
    {
        return px;
    }
 
	// 是否唯一数据源
    bool unique() const BOOST_SP_NOEXCEPT
    {
        return pn.unique();
    }
	// 
    long use_count() const BOOST_SP_NOEXCEPT
    {
        return pn.use_count();
    }

    void swap(shared_array<T> & other) BOOST_SP_NOEXCEPT
    {
        std::swap(px, other.px);
        pn.swap(other.pn);
    }
 
private:

    template<class Y> friend class shared_array;

    T * px;                     // contained pointer
    detail::shared_count pn;    // reference counter 引用计数器

};  // shared_array

7、弱指针

7.1、概述

上述介绍的智能指针都能在不同的场景下独立使用。 相反,弱指针只有在配合共享指针一起使用时才有意义。 弱指针 boost::weak_ptr 的定义在 boost/weak_ptr.hpp 里。boost::weak_ptr 一定是通过 boost::shared_ptr 来初始化的。一旦初始化之后,它基本上只提供一个有用的方法: lock()。此方法返回的初始化弱指针的共享指针。 如果这个共享指针不含有任何对象,返回的共享指针也将是空的。也就是这个方法返回初始化弱指针的指针对象。通过源码也可以看出来,如下代码段。


     shared_ptr<T> lock() const BOOST_SP_NOEXCEPT 
    { 
        return shared_ptr<T>( *this, boost::detail::sp_nothrow_tag() ); 
    } 

当函数需要一个由共享指针所管理的对象,而这个对象的生存期又不依赖于这个函数时,就可以使用弱指针。 只要程序中还有一个共享指针掌管着这个对象,函数就可以使用该对象。 如果共享指针复位了,就算函数里能得到一个共享指针,对象也不存在了。以下示例,使用boost官方提供的示例说明弱指针的使用。

7.2、使用示例
#include <windows.h> 
#include <boost/shared_ptr.hpp> 
#include <boost/weak_ptr.hpp> 
#include <iostream> 

DWORD WINAPI reset(LPVOID p) 
{ 
  boost::shared_ptr<int> *sh = static_cast<boost::shared_ptr<int>*>(p); 
  sh->reset(); 
  return 0; 
} 

DWORD WINAPI print(LPVOID p) 
{ 
  boost::weak_ptr<int> *w = static_cast<boost::weak_ptr<int>*>(p); 
  boost::shared_ptr<int> sh = w->lock(); 
  if (sh) 
    std::cout << *sh << std::endl; 
  return 0; 
} 

int main() 
{ 
  boost::shared_ptr<int> sh(new int(99)); 
  boost::weak_ptr<int> w(sh); 
  HANDLE threads[2]; 
  threads[0] = CreateThread(0, 0, reset, &sh, 0, 0); 
  threads[1] = CreateThread(0, 0, print, &w, 0, 0); 
  WaitForMultipleObjects(2, threads, TRUE, INFINITE); 
} 

main() 函数中,通过 Windows API 创建了2个线程。第一个线程函数 reset() 的参数是一个共享指针的地址。 第二个线程函数 print() 的参数是一个弱指针的地址。 这个弱指针是之前通过共享指针初始化的。一旦程序启动之后,reset()print() 都开始执行。 不过执行顺序是不确定的。 这就导致了一个潜在的问题:reset() 线程在销毁对象的时候 print() 线程可能正在访问它。通过调用弱指针的 lock() 函数可以解决这个问题:如果对象存在,那么 lock() 函数返回的共享指针指向这个合法的对象。否则,返回的共享指针被设置为0,这等价于标准的null指针。

7.3、源码分析

template<class T> class weak_ptr
{

public:

    typedef typename boost::detail::sp_element< T >::type element_type;

	// 构造函数
    BOOST_CONSTEXPR weak_ptr() BOOST_SP_NOEXCEPT : px(0), pn()
    {
    }

    BOOST_SP_NOEXCEPT : px( r.px ), pn( r.pn )
    {
        boost::detail::sp_assert_convertible< Y, T >();
    }

    // 有参构造函数,只能使用shared_ptr和weak_ptr初始化
    template<class Y> 
	weak_ptr(shared_ptr<Y> const & r, element_type * p) BOOST_SP_NOEXCEPT: px( p ), pn( r.pn )
    {
    }

    template<class Y> weak_ptr(weak_ptr<Y> const & r, element_type * p) BOOST_SP_NOEXCEPT: px( p ), pn( r.pn )
    {
    }
	
	// lock函数,返回shared_ptr对象
    shared_ptr<T> lock() const BOOST_SP_NOEXCEPT
    {
        return shared_ptr<T>( *this, boost::detail::sp_nothrow_tag() );
    }

	// 引用计数器爽
    long use_count() const BOOST_SP_NOEXCEPT
    {
        return pn.use_count();
    } 
private:
    element_type * px;            // contained pointer
    boost::detail::weak_count pn; // reference counter

};  // weak_ptr

通过源码可以看出,boost::weak_ptr 只能使用 ==boost::shared_ptr==和 boost::weak_ptr 初始化。弱指针本身对于对象的生存期没有任何影响。 lock() 返回一个共享指针,在多线程使用中是线程安全的。

8、介入式指针

8.1、概述

从上文可知, boost::shared_ptr 在内部记录着引用到某个对象的共享指针的数量,但是对介入式指针来说却没有这一个功能,程序员需要自己做记录。这对于开发框架对象来说特别有用,因为它们需要记录着自身被引用的次数。以下源码使用 COM组件提供的函数做演示。

8.2、使用示例
#include <boost/intrusive_ptr.hpp> 
#include <atlbase.h> 
#include <iostream> 

void intrusive_ptr_add_ref(IDispatch *p) 
{ 
  p->AddRef(); 
} 

void intrusive_ptr_release(IDispatch *p) 
{ 
  p->Release(); 
} 

void check_windows_folder() 
{ 
  CLSID clsid; 
  CLSIDFromProgID(CComBSTR("Scripting.FileSystemObject"), &clsid); 
  void *p; 
  CoCreateInstance(clsid, 0, CLSCTX_INPROC_SERVER, __uuidof(IDispatch), &p); 
  boost::intrusive_ptr<IDispatch> disp(static_cast<IDispatch*>(p)); 
  CComDispatchDriver dd(disp.get()); 
  CComVariant arg("C:\\Windows"); 
  CComVariant ret(false); 
  dd.Invoke1(CComBSTR("FolderExists"), &arg, &ret); 
  std::cout << (ret.boolVal != 0) << std::endl; 
} 

void main() 
{ 
  CoInitialize(0); 
  check_windows_folder(); 
  CoUninitialize(); 
} 

COM 对象是使用 boost::intrusive_ptr 的绝佳范例,因为 COM 对象需要记录当前有多少指针引用它。 通过调用 AddRef()Release() 函数,内部的引用计数分别增 1 或者减 1。当引用计数为 0 时,COM 对象自动销毁。

在 intrusive_ptr_add_ref() 和 intrusive_ptr_release() 内部调用 AddRef()Release() 这两个函数,来增加或减少相应 COM 对象的引用计数。 通过这个对象可以访问底层的文件系统,比如检查一个给定的目录是否存在。 在上例中,我们检查 C:\Windows 目录是否存在。 关键点在于一旦介入式指针 disp 离开了它的作用域——check_windows_folder() 函数的末尾,函数 intrusive_ptr_release() 将会被自动调用。

8.3、源码分析

template<class T> class intrusive_ptr
{
public:

    typedef T element_type;

    BOOST_CONSTEXPR intrusive_ptr() BOOST_SP_NOEXCEPT : px( 0 )
    {
    }

    intrusive_ptr( T * p, bool add_ref = true ): px( p )
    {
        if( px != 0 && add_ref ) intrusive_ptr_add_ref( px );
    }


    intrusive_ptr(intrusive_ptr const & rhs): px( rhs.px )
    {
        if( px != 0 ) intrusive_ptr_add_ref( px );
    }

    ~intrusive_ptr()
    {
        if( px != 0 ) intrusive_ptr_release( px );
    }


private:

    T * px;
};

通过源码分析,boost::intrusive_ptr 无引用计数器,方便外部调用者自定义计数器使用。在其他地方和 boost::shared_ptr 无差别。

9、指针容器

9.1、概述

通过介绍Boost C++ 库的各种智能指针之后,我们应该能够编写安全的代码,来使用动态分配的对象和数组。如果需要将智能对象存储在容器中,下面代码段可以直接实现。

  std::vector<boost::shared_ptr<int> > v; 
  v.push_back(boost::shared_ptr<int>(new int(1))); 
  v.push_back(boost::shared_ptr<int>(new int(2)));

上面例子中的代码是正确的,智能指针确实可以这样用,然而因为某些原因,实际情况中并不这么用。 第一,反复声明 boost::shared_ptr 需要更多的输入。 其次,将 boost::shared_ptr 拷贝,需要频繁的增加或者减少内部引用计数,降低效率。 由于这些原因,Boost C++ 库提供了 指针容器 专门用来管理动态分配的对象。

9.2、使用示例
#include <boost/ptr_container/ptr_vector.hpp> 

int main() 
{ 
  boost::ptr_vector<int> v; 
  v.push_back(new int(1)); 
  v.push_back(new int(2)); 
} 

boost::ptr_vectorboost::shared_ptr 初始化方式类似。== boost::ptr_vector== 用于动态分配的对象,它使用起来更容易也更高效。 boost::ptr_vector 独占它所包含的对象,因而容器之外的共享指针不能共享所有权,这跟 std::vector<boost::shared_ptr > 相反。

10、写在结尾

本文是博主曾经学习 智能指针 时总结的笔记,由于时间比较久远,可能部分功能没有更详细的讲解,后续有时间会重新review,继续完善。指针 作为C/C++语言重要的特性之一,在使用中很容易出现疏忽释放内存的现象,导致内存溢出。而博主之前接触的如C#、Java等高级语言,几乎不需要考虑释放内存。这对于那些放弃C/C++,转投“高级”语言的同学而言,是个巨大的借口。但是,C++后来出现了 智能指针 ,很大程度上弥补了这一繁琐的“短板”。在使用中也有种更“高级”语言的感觉。不管是什么语言,只要学精通,结果都是差不多的,只是在使用上“仁者见仁智者见智”罢了。希望当初下决定学习C/C++的你我,不忘初心,继续走好这条“无聊又刺激”的编程之路。

废话不多说了,如果你在阅读中发现本文有错误,请随时联系博主修改完善,帮助他人,一起学习。如果对你有帮助,请点赞支持我,在编码的路上,一起加油。

参考文档

http://theboostcpplibraries.com/

发布了29 篇原创文章 · 获赞 340 · 访问量 22万+

猜你喜欢

转载自blog.csdn.net/Marble_ccp/article/details/105606236