boost::intrusive_ptr

在介绍llvm::IntrusiveRefCntPtr之前,先介绍intrusive_ptr的概念,intrusive_ptr也是智能指针的一种,同样避免内存泄漏的安全问题,并且intrusive_ptr也是基于RAII实现的。但intrusive_ptrshared_ptr也有一些不同,最本质不同就是计数器存放的位置。对于std::shared_ptr,计数器并不是对象的数据成员,而是std::shared_ptr的数据成员,如下图所示,当然有时候Object和count所占用的内存是一起分配的,例如当使用std::make_shared的时候。

intrusive_ptr中的计数器是作为对象的数据成员存在的,并且intrusive_ptr只是提供了增减计数器的接口。intrusive_ptr中计数器与对象的示意图如下:

为了能够使count作为对象的数据成员存在,一种可行的实现方案是使所有欲使用instrusive_ptr的对象继承自一个专门"提供"count的类型,这个类型在boost中就是basic_intrusive_ref_counter,如下:

namespace boost {struct thread_unsafe_counter;struct thread_safe_counter;template<class DrivedT, class CounterPolicyT = thread_safe_counter>class intrusive_ref_counter{public:intrusive_ref_counter() = noexecpt;intrusive_ref_counter(intrusive_ref_counter *r) = noexcept;intrusive_ref_counter & operator=(intrusive_ref_counter const & r) noexcept;unsigned int use_count() const noexcept;protected:~intrusive_ref_counter() = default;};
}

提供count的任务从智能指针转移到boost::intrusive_ref_counter,那么boost::intrusive_ptr主要就是进行做好计数的工作,关于boost::intrusive_ptr的描述如下:

The intrusive_ptr class template stores a pointer to an object with an embedded reference count. Every new intrusive_ptr instance increments the reference count by using an unqualified call to the function intrusive_ptr_add_ref, passing it the pointer as an argument. Similarly, when an intrusive_ptr is destroyed, it calls intrusive_ptr_release; this function is responsible for destroying the object when its reference count drops to zero. The user is expected to provide suitable definitions of these two functions.

从上面的描述中我们得到如下几点信息:

  • intrusive_ptr本质上存储的是一个指向对象的指针,其中这个对象“内嵌“了计数器
  • 每当intrusive_ptr由对象A创建时,就将对象A的计数加一
  • 每当intrusive_ptr销毁时,其创建时对象的计数就减一,当计数减到0时,将该对象销毁
  • 其中计数加一和减一的函数,intrusive_ptr_add_ref()intrusive_ptr_release()的实现由用户提供

instrusive_ptr的声明大致如下:

namespace boost {template<class T> element_type;intrusive_ptr(); // never throwsintrusive_ptr(T *p, bool add_ref = true);intrusive_ptr(intrusive_ptr const & r)ltemplate<class Y> intrusive_ptr(intrusive_ptr<T> const &r);~intrusive_ptr();T & operator*() const;T *operator->() const;T *get() const;T *detach();
};

其中对于上述的构造函数和析构函数,细节如下:

intrusive_ptr(T * p, bool add_ref = true);
Effects: if (p != 0 && add_ref) intrusive_ptr_add_ref(p);
Postconditions: get() == p

intrusive_ptr(intrusive_ptr const & r);
template<class Y> intrusive_ptr(intrusive_ptr<Y> const & r);
Effects: if (r.get() != 0) intrusive_ptr_add_ref(r.get());
Postconditions: get() == r.get()

~intrusive_ptr();
Effects: if(get() != 0) intrusive_ptr_release(get())

另外对于intrusive_ptr_add_ref()intrusive_ptr_release(),如果编译器支持ADL(Argument Dependent Lookup),就会在它们的参数所定义的namespace中查找二者的定义,否早它们必须定义在boost namespace中。

intrusive_ptr vs std::shared_ptr

在intrusive_ptr class template中有如下两段描述,第一段描述使用intrusive_ptr的主要原因,第二段说明在intrusive_ptrstd::shared_ptr相同的情况下,尽量使用std::shared_ptr

The main reasons to use intrusive_ptr are:

  • Some existing frameworks or Oses provide objects with embedded reference counts;
  • The memory footprint of intrusive_ptr is the same as the corresponding raw pointer;
  • intrusive_ptr<T> can be constructed from an arbitrary raw pointer of type T*.

As a general rule, if it isn’t obvious whether intrusive_ptr better fits your needs than shared_ptr, try a shared_ptr-based design first.

对于第一段中第二个原因需要说明一下,为什么intrusive_ptr占用的内存比shared_ptr少,原因在于std::shared_ptr理念上对象和计数的存放是分开的,所以大部分情况下每个std::shared_ptr有两个指针的内存开销,一个指针指向control block(存储引用计数等信息),一个指针指向对象。之所以说大部分情况,是因为std::make_shared<>会统一分配一整块内存存储计数和对象,此时就只需存储一个指针。

Memory footprint refers to the amount of main memory that a program uses or references while running. [Memory footprint]

std::shared_ptr中的std::make_shared<>

前面提到了std::make_shared<>会为计数和对象分配一整块内存,虽然标准中没有这样规定,但是由于std::make_shared提供了这样的一个将计数和对象放在一起的机会,所以基本上所有的实现都会这样做。

std::shared_ptr<T>(new T(args…)) performs at least two allocations (one for the object T and one for the control block of the shared pointer), while std::make_shared<T> typically performs only one allocation (the standard recommends, but does not require this, all known implementations do this). [make_shared]

The storage is typically larger than sizeof(T) in order to use one allocation for both the control block of the shared pointer and the T object. The std::shared_ptr constructor called by this function enables shared_from_this with a pointer to the newly constructed object of type T. [make_shared]

std::make_shared所带来的好处和坏处都是由这一个特点引发的,通常意义上使用std::make_shared是好的,不仅包裹了内存的分配,不会将raw pointer暴露出来,避免了访问已释放内存的问题,同时也减少了内存的分配。stackoverflow上有一个相关的问题,见Difference in make_shared and normal shared_ptr in C++,比较详尽。

了解了std::make_shared的好处以后,intrusive_ptr的第三个优点也就显而易见了。使用一个raw pointer来构造std::shared_ptr通常不能保证是安全的,反而更容易出错,因为raw pointer有可能在其他地方被使用,而std::shared_ptr不能为这些使用增加计数或减少计数。而intrusive_ptr就不会存在这个问题,因为raw pointer本身就存储有计数信息。

知乎上有一个intrusive_ptr的问题,为什么 intrusive_ptr 没有进入标准库?,该问题下设提到了std::enable_shared_from_this,由于我在工作中没有遇到很多使用std::enable_shared_from_this的代码场景,理解不够深刻,所以先挖个坑。

llvm::IntrusiveRefCntPtr

待填

llvm:: IntrusiveRefCntPtr相关推荐

  1. 编译器 llvm clang 源码转换示例

    编译器 llvm clang 源码转换示例 从git获取llvm项目的源码方式: git clone https://github.com/llvm/llvm-project.git 下载源码后,进入 ...

  2. LLVM与Clang编译图例

    LLVM与Clang编译图例 参考链接: https://blog.csdn.net/u012874859/article/details/108530256

  3. 三段式LLVM编译器

    三段式LLVM编译器 目录 概述 LLVM技术生态之编译器 一.传统编译器的设计 二.传统编译器模式的实现 三.LLVM的三段式实现 四.LLVM's Code Representation:LLVM ...

  4. LLVM Clang前端编译与调试

    LLVM Clang前端编译与调试 iOS 关于编译 o 一.Objective-C 编译过程 o 为什么需要重新编译? o 编译步骤 o 二.编译步骤的详细说明 o 1.预处理 o 2.编译 o 词 ...

  5. LLVM与Clang局部架构与语法分析

    LLVM与Clang局部架构与语法分析 Clang与LLVM LLVM整体架构,前端用的是clang,广义的LLVM是指整个LLVM架构,一般狭义的LLVM指的是LLVM后端(包含代码优化和目标代码生 ...

  6. LLVM语法语义指令特性

    LLVM语法语义指令特性 High Level Structure Module Structure LLVM 程序由Module's组成,每个 's 是输入程序的一个翻译单元.每个模块由函数,全局变 ...

  7. CPU架构的llvm后端

    Creating an LLVM Backend for the Cpu0 Architecture Backend structure • TargetMachine structure • Add ...

  8. LLVM编译器基础架构与DragonEgg示例

    LLVM编译器基础架构与DragonEgg示例 LLVM 概述 LLVM 项目是模块化和可重用的编译器和工具链技术的集合.LLVM 与传统的虚拟机几乎没有关系."LLVM"这个名字 ...

  9. LLVM数据流分析的理论

    LLVM数据流分析的理论 标量优化(scalar目录): 死代码消除(BDCE.cpp[code],ADCE.cpp[code],DCE.cpp[code]), 全局值编号(GVN.cpp[code] ...

最新文章

  1. 【Matlab】矩阵中选取任意子矩阵
  2. python源码文件_从源代码生成Python文件
  3. python委托模式详细解释
  4. sublime用cmd窗口调试python_Sublime Text设置程序输出窗口为dos窗口
  5. vue结合element实现自定义上传图片、文件
  6. windows/linuxjdk安装,jdk1.6升级到1.7
  7. linux操作命令之压缩命令
  8. 知识点收录04:MAVEN相关的知识点
  9. PIO(编程输入/输出模型)和DAM(直接访问内存)
  10. 鼠标上下滑轮时,来回乱跑
  11. 第四届“云鼎奖”网络投票火热进行中——入围名单一览
  12. day python calss08 深浅copy
  13. win10添加打印机--无法访问指定设备,路径或文件。。
  14. Oracle 重建TEMP表空间
  15. 小博老师解析Java核心技术 ——JDBC普通增删改操作
  16. telnet 访问80端口
  17. ZSTU OJ-4454 招兵买马
  18. 关于petitfatfs的移植问题
  19. springmvc之响应类型,string,void,ModelAndView
  20. React使用echarts-for-react

热门文章

  1. iec104点号_IEC104报文遥测信息提取方法与流程
  2. Amazon SageMaker Data Wrangler 简化数据准备过程,助力机器学习
  3. SKLearn 信用卡欺诈检测(creditcard)
  4. 曲线斜率与法向量综合辨析
  5. java多线程霓虹灯,Android 霓虹灯
  6. 计算机关闭更新配置win10,windows10取消自动更新怎么操作_win10怎样取消自动更新...
  7. 模运算(包含模的逆运算)
  8. 数据库中limit的 用法
  9. limit , 与 limit offset 的区别
  10. SpringBoot美化日志输出——自定义Banner艺术字体