指针智能是RAII的思想的具体体现。利用对象生命周期来管理资源。
在C++11中,引入shared_ptr、weak_ptr和unique_ptr。
share_ptr是一个能有效解决赋值和拷贝构造的引用技术。
std::shared_ptr
通过引用计数的方式来管理对象的生命周期,但是如果两个对象互相持有对方的std::shared_ptr
,就会产生循环引用,导致引用计数永远不会降到零,从而引发内存泄漏。
循环引用产生
俩个对象互相持有对方的shared_ptr 导致计数永远不会降到零
下面看一个例子:
在这个例子中,定义了一个结构体Node,它的prev和next分别是shared_ptr类型
有俩个对象node1和node2,node1的next是node2,node2的prev是node1
俩者都持有对方的指针。
node1创建时count为1,被赋值给 node2的prev后,count为2.
同时node2的count初始为1,被赋值后为2;
通过智能指针托管node1和node2在对象生命周期结束后,应该由析构函数完成清理工作。
在进程结束后,并没有触发析构函数
分析
计数没有被释放到0
Node1对象创建后 Node2创建。
出了作用域先析构Node2结点。share_ptr的析构会自减引用计数,由2减到1,引用计数不为0,不发生其它操作;
析构Node1的结点析构,引用计数减到1,不发生其它操作;
之后Node1和Node2的引用计数都是1;
右边结点的空间什么时候销毁?左边结点的next销毁.
左边结点的next什么时候销毁?左边结点销毁
左边结点什么时候销毁?右边结点的prev销毁。
右边结点的prve什么时候销毁。右边结点的销毁。
经过几次往复后,右边结点也不会被销毁,存在循环引用的问题。
那么当循环引用出现后,即使进程结束,资源也不会被回收。
weak_ptr
解决循环引用的问题,就要针对俩个对象持有对方的shared_ptr特殊处理。
std::weak_ptr
是C++标准库中的一种智能指针,它设计用来解决std::shared_ptr
之间循环引用的问题。
weak_ptr
持有对一个由shared_ptr
管理的对象的非拥有(弱)引用,这意味着weak_ptr
不会增加对象的引用计数。
这样,即使存在循环引用,对象的生命周期仍然能够由shared_ptr
正确管理,当最后一个shared_ptr
被销毁时,相关联的资源会被释放。
weak_ptr就是辅助shared_ptr完成循环引用的问题。不会增加计数。
下面修改程序
将prev和next修改为weak_ptr<ListNode>弱引用。计数不会被增加,所以在赋值后,计数是1。
weak_ptr的特点
- 不是常规的智能指针,不支持RAII,是辅助share_ptr完成循环引用问题。
- 支持像指针一样->和*
- 即便
weak_ptr
指向的对象被销毁了,对weak_ptr
的使用也是安全的,它不会悬挂指向无效内存。
总结
简而言之
weak_ptr就像一个观察者,不会对对象的生存产生影响,也不会因为对象的销毁而无效。
但是试图通过weak_ptr访问一个不存在的对象的操作会失败。
观察者可以识别到内存是否存在。