3.4 Boost库intrusive_ptr智能指针的使用

intrusive_ptr 也是一种引用计数型智能指针,但与之前介绍的 scoped_ptr 和 shared_ptr 不同,需要额外增加一些代码才能使用它。它的名字可能会给人造成误解,实际上它并不一定要"侵入"代理对象的内部修改数据。

如果现在代码已经有了引用计数机制管理的对象,那么 intrusive_ptr 是一个非常好的选择,它可以包装已有对象从而得到与 shared_ptr 类似的智能指针。

intrusive_ptr 的类摘要如下:

cpp 复制代码
template<class T> class intrusive_ptr {
public:
    typedef T element_type; //被代理的对象

    intrusive_ptr(); //构造函数
    intrusive_ptr(T * p, bool add_ref = true);
    intrusive_ptr(intrusive_ptr const & r);
    template<class Y> intrusive_ptr(intrusive_ptr<Y> const & r);
    ~intrusive_ptr();

    intrusive_ptr & operator=(intrusive_ptr const & r);
    template<class Y> intrusive_ptr & operator=(intrusive_ptr<Y> const & r);
    intrusive_ptr & operator=(T * r);

    void reset(); //重置指针
    void reset(T * r);
    void reset(T * r, bool add_ref);

    T & operator*() const; //操作符重载
    T * operator->() const;
    explicit operator bool() const;

    T * get() const;
    T * detach();
    void swap(intrusive_ptr & b);
};

因为 intrusive_ptr 也是引用计数型指针,所以它的接口与 shared_ptr 很像,它同样支持比较操作,以及 static_pointer_cast() 、dynamic_pointer_cast() 等转型操作,但它自己不直接管理引用计数,而是调用以下两个函数来间接管理引用计数:

cpp 复制代码
void intrusive_ptr_add_ref(T * p); //增加引用计数
void intrusive_ptr_release(T * p); //减少引用计数

intrusive_ptr 的构造函数和 reset() 相比还多出一个 add_ref 参数,它表示是否增加引用计数,如果 add_ref==false ,那么它就相当于 weak_ptr ,只是简单地观察对象。

假设我们已经有了一个自己实现引用计数的类 counted_data:

cpp 复制代码
struct counted_data    //自己实现引用计数
{
    int m_count = 0;    //引用计数
    ...    //其他数据成员
};

为了让 intrusive_ptr 正常工作,我们需要实现它要求的两个回调函数:

cpp 复制代码
void intrusive_ptr_add_ref(counted_data* p)    //增加引用计数
{
    ++p->m_count;
}

void intrusive_ptr_release(counted_data* p)    //减少引用计数
{
    if(--p->m_count == 0)
    {
        delete p;    //引用计数为 0 则删除指针
    }
}

需要注意的是在 intrusive_ptr_release() 函数中必须检查引用计数,因为 intrusive_ptr 不负责销毁实例,所以这个工作必须由我们自己完成。

实现 intrusive_ptr_add_ref()/intrusive_ptr_release() 后,intrusive_ptr 就可以管理 counted_data 了,示例代码如下:

cpp 复制代码
#include <iostream>
#include <string>
#include <Windows.h>
#include <boost/smart_ptr.hpp>

using namespace std;

struct counted_data    //自己实现引用计数
{
	int m_count = 0;    //引用计数
};
void intrusive_ptr_add_ref(counted_data* p)    //增加引用计数
{
	++p->m_count;
}

void intrusive_ptr_release(counted_data* p)    //减少引用计数
{
	if (--p->m_count == 0)
	{
		delete p;    //引用计数为 0 则删除指针
	}
}
int main()
{
	cout << "Start" << endl;

	typedef boost::intrusive_ptr<counted_data> counted_ptr; //类型定义
	counted_ptr p(new counted_data);    //创建智能指针
	assert(p);    //bool 转型
	cout << "创建对象p后计数:" << p->m_count << endl;   //operator->

	counted_ptr p2(p);    //指针拷贝构造
	cout << "拷贝构造对象p2后p的计数:" << p->m_count << endl;    //引用计数增加

	counted_ptr weak_p(p.get(), false);    //弱引用
	cout << "弱拷贝构造对象weak_p后p的计数:" << weak_p->m_count << endl;    //引用计数不增加

	p2.reset();    //复位指针
	assert(!p2);    //p2 不持有指针
	cout << "释放拷贝构造对象p2后p的计数:" << p->m_count << endl;    //引用计数减少

	cout << "End" << endl;
	system("pause");
	return 0;
}

运行结果:

cpp 复制代码
Start
创建对象p后计数:1
拷贝构造对象p2后p的计数:2
弱拷贝构造对象weak_p后p的计数:2
释放拷贝构造对象p2后p的计数:1
End

可以看到,只需要编写少量代码,我们就可以复用既存的数据结构,获得一个与shared_ptr的用法几乎一样的智能指针,而且这样并没有增加多余的"开销",这在某些对性能要求比较苛刻的场景里非常实用。

但大多数情况下,shared_ptr完全不必增加新代码,而且它的灵活性更高,使用intrusive_ptr前必须要确定它能够带来足够多的好处。

为了进一步简化实现引用计数的工作,intrusive_ptr 在头文件<boost/smart_prt/intrusive_ref_counter.hpp> 里定义了一个辅助类 intrusive_ref_counter :

cpp 复制代码
template<typename Derived, typename CounterPolicyT = thread_safe_counter>
class intrusive_ref_counter {
private:
    typedef typename CounterPolicyT::type counter_type;
    mutable counter_type m_ref_counter;
    
public:
    intrusive_ref_counter();
    unsigned int use_count() const;
    
protected:
    ~intrusive_ref_counter() = default;
    
    friend void intrusive_ptr_add_ref(const intrusive_ref_counter* p);
    friend void intrusive_ptr_release(const intrusive_ref_counter* p);
};

intrusive_ref_counter 内部定义了一个计数器变量 m_ref_counter ,使用模板参数配置策略类实现了引用计数的增减,默认的策略是线程安全的 thread_safe_counter 。

intrusive_ref_counter 需要被继承使用,这样其子类就会自动获得引用计数的能力,之前的 counted_data 可以简化为如下代码:

cpp 复制代码
#include <iostream>
#include <string>
#include <Windows.h>
#include <boost/smart_ptr.hpp>
#include <boost/smart_ptr/intrusive_ref_counter.hpp>

using namespace std;

struct counted_data :public boost::intrusive_ref_counter<counted_data>   //自己实现引用计数
{
	int m_count = 0;    //引用计数
};

int main()
{
	cout << "Start" << endl;

	typedef boost::intrusive_ptr<counted_data> counted_ptr; //类型定义
	counted_ptr p(new counted_data);    //创建智能指针
	assert(p);    //bool 转型
	cout << "创建对象p后计数:" << p->use_count() << endl;   //operator->

	counted_ptr p2(p);    //指针拷贝构造
	cout << "拷贝构造对象p2后p的计数:" << p->use_count() << endl;    //引用计数增加

	counted_ptr weak_p(p.get(), false);    //弱引用
	cout << "弱拷贝构造对象weak_p后p的计数:" << weak_p->use_count() << endl;    //引用计数不增加

	p2.reset();    //复位指针
	assert(!p2);    //p2 不持有指针
	cout << "释放拷贝构造对象p2后p的计数:" << p->use_count() << endl;    //引用计数减少

	cout << "End" << endl;
	system("pause");
	return 0;
}

代码运行结果:

cpp 复制代码
Start
创建对象p后计数:1
拷贝构造对象p2后p的计数:2
弱拷贝构造对象weak_p后p的计数:2
释放拷贝构造对象p2后p的计数:1
End
相关推荐
程序猿20232 小时前
Python每日一练---第六天:罗马数字转整数
开发语言·python·算法
装不满的克莱因瓶2 小时前
【Java架构师】各个微服务之间有哪些调用方式?
java·开发语言·微服务·架构·dubbo·restful·springcloud
杨筱毅2 小时前
【穿越Effective C++】条款13:以对象管理资源——RAII原则的基石
开发语言·c++·effective c++
Zz_waiting.3 小时前
统一服务入口-Gateway
java·开发语言·gateway
四维碎片3 小时前
【Qt】大数据量表格刷新优化--只刷新可见区域
开发语言·qt
薛慕昭4 小时前
C语言核心技术深度解析:从内存管理到算法实现
c语言·开发语言·算法
火星数据-Tina4 小时前
Python + WebSocket 实现实时体育比分系统(含数据库设计与前端演示)
开发语言·前端
⑩-4 小时前
浅学Java-设计模式
java·开发语言·设计模式
攻心的子乐4 小时前
软考 关于23种设计模式
java·开发语言·设计模式