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
相关推荐
郑州光合科技余经理2 天前
代码展示:PHP搭建海外版外卖系统源码解析
java·开发语言·前端·后端·系统架构·uni-app·php
feifeigo1232 天前
matlab画图工具
开发语言·matlab
dustcell.2 天前
haproxy七层代理
java·开发语言·前端
norlan_jame2 天前
C-PHY与D-PHY差异
c语言·开发语言
多恩Stone2 天前
【C++入门扫盲1】C++ 与 Python:类型、编译器/解释器与 CPU 的关系
开发语言·c++·人工智能·python·算法·3d·aigc
QQ4022054962 天前
Python+django+vue3预制菜半成品配菜平台
开发语言·python·django
遥遥江上月2 天前
Node.js + Stagehand + Python 部署
开发语言·python·node.js
蜡笔小马2 天前
21.Boost.Geometry disjoint、distance、envelope、equals、expand和for_each算法接口详解
c++·算法·boost
m0_531237172 天前
C语言-数组练习进阶
c语言·开发语言·算法
Railshiqian2 天前
给android源码下的模拟器添加两个后排屏的修改
android·开发语言·javascript