智能指针的源码都在 《memory》 头文件中。因为头文件太长,再者本次整理是基于以前的零散的模板分析。故相当于抽取了该头文件中关于智能指针的源码进行分析,注释。
(1 探讨一)当独占指针指向数组时,其默认的删除器是哪个?相关模板的定义轮廓如下:
我们要分析、猜测编译器在泛型推导时是如何确定模板参数的类型的。先给出一段源码,如下:
测试如下:
以及:
以及:
STL 库代码肯定是不会错的。可以得出结论啊: unique_ptr 声明时的模板参数 _Ty 的含义不同于模板定义时的模板参数 _Ty, 模板声明时的删除器的默认值是对第一个模板实参的整体取 default_delete<_Ty[]> ,所以选择了正确的删除器模板。
(2 探讨二) 独占指针可以指向数组,对其成员函数 reset 的形参,应该提供数组指针,而不是普通指针。源码摘抄如下:
增加一些打印语句:
以及:
但又测试了一下:
(3 探讨三) 独占指针的内存模型。独占指针的构造还是比较简单的,因为不需要考虑资源共享,独占指针其实就是对裸指针的封装。摘抄的源代码如下:
(4 探讨四) 对 make_unique 函数的形参,若创建指向数组的独占指针,函数形参是数组的长度;若创建指向普通对象的独占指针,函数形参是要传递给对象的构造函数的。依据源码如下:
该图中出现了 extent_V 模板的使用,其源码如下:
比较难以理解,给出几个测试结果如下:、
(5 探讨五) 本条探讨共享指针 shared_ptr 的内存模型。读代码,主要要先掌握弄清楚一个类的数据成员。这些指针中最难的当属共享指针。根据源码得出的结论如下(也附带给出弱指针 weak_ptr 的内存模型):
(6 探讨六)已知一个裸指针指向堆区的一个对象。共享指针可以根据裸指针构造 shared_ptr ,即存在这样的构造函数:
但不允许用裸指针构造弱指针 weak_ptr ,对弱指针不存在上面的类似的构造函数;但可以根据共享指针来构造弱指针,即存在:
而且根据源码得知:即使仅有一个共享指针指向堆区对象,该对象上的弱引用数量也初始化为一;若再根据共享指针构造一个弱指针,则该对象上的弱引用数量就为 2 。这没有为什么,源码就是这么写的。以 1 为弱引用的计数起点,肯定有其自圆其说的地方。
补充:强引用计数是为了管理智能指针指向的对象 ,弱引用计数是为了管理也在堆区中分配的 _Ref_count_base 的子类对象。
测试如下:
对断点调试,当仅创建一个共享指针时:
当继续 F10 创建一个弱引用后:
(7 探讨七) 允许创建空的共享指针,弱指针和独占指针,测试代码如下:
因为一般咱们认为这些智能是要绑定到某个对象上的。但确实智能指针不必绑定到任何对象。因为 STL库中其模板都有空的默认构造函数:
以及:
以及:
以及具有 nullptr 默认值的父类:
(8 探讨八) 王建伟老师的课本里教的 用 make_shared 创建智能指针的效率更高。因为根据裸指针创建共享指针时候,是分两次分配内存,一次创建堆区待指向的对象,再次在堆区申请内存以创建引用控制块。而用 make_shared 函数,只申请一次堆区内存,创建了包含对象和其控制块的更大的对象。相应也减少了调用对象的构造和析构函数的次数,所以效率更高。是这样的。源码轮廓如下:
(9 探讨九) 依据弱指针创建共享指针是可以的。源代码里有这样的构造函数,其也会检查弱指针指向的对象是否还存在,否则再创建共享指针是违法的。代码依据如下:
再给出其调用链:
以及:
当然也可以用弱指针的成员函数 lock 创建共享指针,函数体的代码是一样的:
(10 探讨十) 重新思考下继承了 enable_shared_from_this 的类。咱们知道,根据指向对象 A 的裸指针连续两次构造共享指针,就出错了。若 A 继承了 enable_shared_from_this ,则可以调用 enable_shared_from_this 的 的成员函数来创建共享指针,实现了根据裸指针创建共享指针的那么个意思。核心就是因为 enable_shared_from_this 中 有一个数据成员 weak_ptr ,此弱指针包含了对象 A 的控制块信息。从而保证系统中创建的所有共享指针,共用唯一的引用控制块。从而保证对对象 A 的正确管理与析构。
先大概看一下 enable_shared_from_this 的构造与析构, copy 构造与赋值运算符的语义,其与共享指针、对象 A 的析构息息相关:
以下列出参考代码,待会分析相关对象的创建与析构:
接着依据上面的构造与析构函数举例分析:
另外补充下上面相关基类的析构函数,大部分都为空的其实。
(11 探讨十一) 独占指针的 get() 与 release( ) 函数的区别:
谢谢