介绍三种最常用的智能指针:

  1. shared_ptr:对象生成期自动管理,基于共享所有权(适合用于标准库容器)
  2. weak_ptr:安全观测共享资源,配合shared_ptr
  3. intrusive_ptr:带有一个侵入式引用计数的对象的共享所有权。

介绍 shared_ptr( <boost/shared_ptr.hpp> )

shared_ptr类的实现说明

template<class Y> explicit shared_ptr(Y * p = 0);
// 注:p指向有效资源,比如 new int(5)
 
template<class Y, class D> shared_ptr(Y * p, D d); 
// d(p)形式释放资源

shared_ptr(shared_ptr const & r); // 对应shared_ptr<T>
 
template<class Y>
shared_ptr(shared_ptr<Y> const & r);//支持多态,只要Y*能转化为T*
 
template<class Y>
explicit shared_ptr(weak_ptr<Y> const & r);
// 注:从有效的观察者weak_ptr绑定资源(如果weak_ptr为空,抛出bad_weak_ptr异常)

比如:

X * CreateX();
void DestroyX(X *);

// 可靠地销毁一个由 CreateX 返回的指针的唯一方法就是调用 DestroyX。
shared_ptr<X> createX()
{
    shared_ptr<X> px(CreateX(), DestroyX);// 释放资源制定
    return px;
}
shared_ptr & operator=(shared_ptr const & r);
 
template<class Y> shared_ptr & operator=(shared_ptr<Y> const & r);     
 
void reset();  // 指针失去当前对象所有权
 
template<class Y> void reset(Y * p);
// 注:引用数减1,绑定新资源
 
T & operator*() const;
 
T * operator->() const;
 
T * get() const;  // 返回裸指针(资源地址)
 
bool unique() const;
 
long use_count() const;  
 
operator unspecified-bool-type() const;// 代理,能用到需要bool的环境
 
void swap(shared_ptr & b);
template<class T, class U>   
bool operator==(shared_ptr<T> const & a, shared_ptr<U> const & b);
// a.get() == b.get()
 
template<class T, class U>   
bool operator!=(shared_ptr<T> const & a, shared_ptr<U> const & b);
 
template<class T>
void swap(shared_ptr<T> & a, shared_ptr<T> & b);
 
template<class T>
T * get_pointer(shared_ptr<T> const & p); // 获取裸指针

shared_ptr的使用

例子:

#include <iostream>
using namespace std;
#include <boost/shared_ptr.hpp>
using namespace boost;
 
struct A
{
    A(){ cout << "A()" << endl; }
    ~A() { cout << "~A()" << endl; } // 程序结束后没有析构掉
    shared_ptr<A> ptr;
};
int main()
{
    shared_ptr<A> p( new A ); // A引用计数1
    p->ptr = p; // A引用计数2
 
    return 0;
}

对象p析构时,引用计数减1,然后检查发现非0,不做delete操作,于是内存泄漏。

比如:

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

因为函数参数的求值顺序是不确定的,new int(2) 首先被求值,g()可能是第二个被求值,如果 g 抛出一个异常,我们永远也不可能到达 shared_ptr 的构造函数。于是内存泄漏。

介绍 weak_ptr( <boost/weak_ptr.hpp> )

weak_ptr的实现说明

weak_ptr是shared_ptr的观察者,只是观察被共享的资源,但不会真正拥有资源(没有所有权)如果要使用资源,用lock或者shared_ptr的构造函数得到所观察资源的shared_ptr<T>对象。

介绍 intrusive_ptr

intrusive_ptr 类模板存储一个指向带有侵入式引用计数的对象的指针。

这两个函数的适当定义由用户提供。intrusive_ptr_add_ref 和 intrusive_ptr_release 应该定义名字空间 boost 中。

使用 intrusive_ptr 的主要原因是:

作为一个通用规则,如果 intrusive_ptr 不是很明显地比 shared_ptr 更加适合你的需要,请首先考虑基于 shared_ptr 的设计。

例子:

#include <iostream>
using namespace std;
 
#include <boost/intrusive_ptr.hpp>
using namespace boost;
 
// functions
template< typename T >
long intrusive_ptr_add_ref( T *p )
{
    return p->AddRef();
}
 
template<typename T>
long intrusive_ptr_release( T *p )
{
    return p->Release();
}
 
template <class T>
inline bool   intrusive_ptr_create(intrusive_ptr<T> & __t_ptr)
{
    intrusive_ptr<T> spObj(T::_CreateInstance(), false);
    __t_ptr = spObj;
    return spObj != 0;
}
 
// class
class ITest
{
public:
    ITest() : count(0) { cout << "ITest()" << endl; }
    ~ITest() { cout << "~ITest()" << endl; }
    void Doing() { cout << "doing" << endl; }
 
public:
    static ITest* _CreateInstance()
    {
       ITest* pObj = new ITest();
       pObj->AddRef();
       return pObj;
    }
    long AddRef() { return ++count; }
    long Release()
    {
       if ( --count == 0 )
       {
           delete this;// 通过new出来的对象才能这样做,栈上的不行
           return 0;
       }
       return count;
    }
private:
    long count;
};
 
int main()
{
    intrusive_ptr<ITest> pObj, pObj2, pObj3;
    if ( intrusive_ptr_create( pObj ) )
    {
       cout << "1: OK" << endl;
    }
 
    pObj2 = pObj;
    pObj->Doing();
 
    if ( intrusive_ptr_create( pObj3 ) )
    {
       cout << "3: OK" << endl;
    }
 
    return 0;
}

智能指针编程技术

pimpl 惯用法 – 避免在头文件中暴露身体

例子:

// 头文件.h
class example
{
public:
    example();
    void do_something();
private:
    class implementation;
    shared_ptr< implementation > _imp; // 隐藏实现细节
};
 
// 实现文件.cpp
class example::implementation
{
public:
    ~implementation() { std::cout << "destroying implementation\n"; }
};
 
example::example() : _imp( new implementation ) {}
 
void example::do_something()
{
    cout << "use_count() is " << _imp.use_count() << "\n";
}

用shared_ptr持有一个指向 COM 对象的指针

即:使用该指针,并且维持其引用计数状态,嵌入式的生成一个shared_ptr,中途持有该指针,但是对之前的代码逻辑没有影响。

COM 对象有一个嵌入式的引用计数和两个操作它的成员函数。AddRef() 增加计数。Release() 减少计数并当计数将为 0 时销毁它。

shared_ptr<IWhatever> make_shared_from_COM(IWhatever * p)
{
    p->AddRef();
    shared_ptr<IWhatever> pw(p, mem_fn(&IWhatever::Release));
    return pw;
}

无论如何,从 pw 创建 shared_ptr 的拷贝不会在 COM 对象的嵌入计数中“登记”,它们将共享在 make_shared_from_COM 中创建的单独的引用。当最后的 shared_ptr 被销毁,从 pw 创建的 weak pointers(弱指针)将失效,无论 COM 对象本身是否还存在。

获得一个指向 this 的 shared_ptr

boost封装的实现, 库中现在有一个辅助类模板 enable_shared_from_this 能被用于实现这种需求的类。

例如:

class X
{
public:
 
    virtual void f() = 0;
 
protected:
 
    ~X() {}
};
 
class Y
{
public:
 
    virtual shared_ptr<X> getX() = 0;
 
protected:
 
    ~Y() {}
};
 
class impl:
public X, public Y, public enable_shared_from_this<impl>
{
public:
 
    impl(impl const &);
    impl & operator=(impl const &);
 
public:
 
    virtual void f() { /* ... */ }
 
    virtual shared_ptr<X> getX()
    {
        return shared_from_this();
    }
}