C++:智能指针(2)——shared_ptr引用计数功能,weak_ptr解决循环引用

上一篇文章 C++:智能指针(1)——auto_ptr, unique_ptr的区别已经介绍了auto_ptr与unique_ptr;如果有疑惑的同学可以查看;

shared_ptr

shared_ptr是原始指针的封装类。它是一个附加了引用计数所有权模型,即它与shared_ptr的所有副本协作维护其包含的指针的引用计数。因此,每当一个新的指针指向资源时,计数器就会增加,而在调用对象的析构函数时,计数器就会减少。

引用计数一种用于存储对资源(例如对象,内存块,磁盘空间或其他资源)的引用,指针或句柄数量的技术。 在引用计数大于零之前,即直到删除了shared_ptr的所有副本之前,原始指针指向的堆内存不会被释放。 因此,当我们要将一个原始指针分配给多个所有者时,应该使用shared_ptr。
源码

template<class _Tp>
class _LIBCPP_TEMPLATE_VIS shared_ptr
{
public:
    typedef _Tp element_type;

#if _LIBCPP_STD_VER > 14
    typedef weak_ptr<_Tp> weak_type;
#endif
private:
    element_type*      __ptr_;
    __shared_weak_count* __cntrl_;
    ...
}

析构函数

template<class _Tp>
shared_ptr<_Tp>::~shared_ptr()
{
    if (__cntrl_)
        __cntrl_->__release_shared();
}

可以看到当计数为0时才释放;

shared_ptr定义了复制构造函数与赋值运算符,所以可以放入STL容器;shared_ptr的赋值不会像auto_ptr导致所有权的转换,而是增加了一个所有权的共享;

shared_ptr的循环引用问题

Circular reference关系(shared_ptr的问题):让我们考虑一个场景,其中我们有两个类A和B,它们都具有指向其他类的指针。因此,总是像A指向B,B指向A。因此,use_count永远不会达到零,也永远不会被删除。
https://www.geeksforgeeks.org/auto_ptr-unique_ptr-shared_ptr-weak_ptr-2/
图转自https://www.geeksforgeeks.org/auto_ptr-unique_ptr-shared_ptr-weak_ptr-2/

//
//  智能指针shared_ptr循环引用.cpp
//  LeetCodePlayground
//
//  Created by 兆吉 王 on 2020/4/15.
//  Copyright © 2020 兆吉 王. All rights reserved.
//

#include <iostream>
#include <memory>
using namespace std;
 
class Parent;
typedef std::shared_ptr<Parent> ParentPtr;
 
class Child
{
public:
    ParentPtr father;
    Child() {
        cout << "hello Child" << endl;
    }
    ~Child() {
        cout << "bye Child\n";
    }
};
 
typedef std::shared_ptr<Child> ChildPtr;
 
class Parent {
public:
    ChildPtr son;
    Parent() {
        cout << "hello parent\n";
    }
    ~Parent() {
        cout << "bye Parent\n";
    }
};
 
void testParentAndChild()
{
/*
 均用shared_ptr指向了new出的对象
 shared_ptr<Parent> noramlp,p 与 shared_ptr<children> c 均在栈上
 对应的new内存 均在堆上
*/
    ParentPtr normalp(new Parent());
    ParentPtr p(new Parent());
    ChildPtr c(new Child());
    p->son = c;
    c->father = p;
    cout << normalp.use_count() << endl;
    cout << p.use_count() << endl;
    cout << c.use_count() << endl;
/*
 当程序返回时
 shared_ptr<Parent> noramlp,p 与 shared_ptr<children> c 栈上的变量被释放
 noramlp的释放导致了对应堆上内存的 引用计数 为0,
 所以释放 ParentPtr normalp(new Parent()); new出来的内存,调用了Parent的~Parent析构函数
 
 p与c同样被栈释放,但是对应堆上内存的 引用计数 为1,
 所以未调用析构函数
*/
}
 
int main()
{
    testParentAndChild();
    return 0;
}

程序修改自https://blog.csdn.net/Xiao_CangTian/article/details/89764858?depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-1&utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-1

输出

hello parent
hello parent
hello Child
1
2
2
bye Parent
Program ended with exit code: 0

当程序返回时
shared_ptr<Parent> noramlp,pshared_ptr<children> c 栈上的变量被释放
noramlp的释放导致了对应堆上内存的 引用计数 为0,
所以释放 ParentPtr normalp(new Parent()); new出来的内存,调用了Parent的~Parent析构函数

p与c同样被栈释放,但是对应堆上内存的 引用计数 为1,
所以p与c的释放无法使对应堆内存的引用计数归0(循环引用),导致两块内存无法释放。可以看到程序结束时并未调用两块内存的析构函数;

weak_ptr

创建weak_ptr作为shared_ptr的副本。它提供对一个或多个shared_ptr实例所拥有但不参与引用计数的对象的访问。 weak_ptr的存在或破坏对shared_ptr或其其他副本没有影响。在某些情况下,需要在shared_ptr实例之间中断循环引用。
https://www.geeksforgeeks.org/auto_ptr-unique_ptr-shared_ptr-weak_ptr-2/
图转自https://www.geeksforgeeks.org/auto_ptr-unique_ptr-shared_ptr-weak_ptr-2/

typedef std::weak_ptr<Parent> WeakParentPtr;
 
class Child
{
public:
    WeakParentPtr father;                 // 只要一环换成 weak_ptr, 即可打破环
    Child() {
        cout << "hello Child" << endl;
    }
    ~Child() {
        cout << "bye Child\n";
    }
};

修改类内智能指针为weak_ptr后,输出:

hello parent
hello parent
hello Child
1
2
bye Parent
bye Parent
bye Child
Program ended with exit code: 0

weak_ptr存在的意义

weak_ptr 是为了辅助shared_ptr而引入的一种智能指针,它存在的意义就是协助shared_ptr更好的完成工作,我们可以把它比做成一个秘书或助理;
weak_ptr的构造和析构并不会改变引用计数的大小,它可以由一个shared_ptr或weak_ptr的对象构造获得。它没有对“*”和“->”的重载,但可以使用lock获得一个可用的shared_ptr对象

参考链接

https://blog.csdn.net/Xiao_CangTian/article/details/89764858?depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-1&utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-1#1.3%20%E8%A7%A3%E5%86%B3%E7%B1%BB%E4%B9%8B%E9%97%B4%E5%BE%AA%E7%8E%AF%E5%BC%95%E7%94%A8

https://www.geeksforgeeks.org/auto_ptr-unique_ptr-shared_ptr-weak_ptr-2/

https://blog.csdn.net/LLZK_/article/details/52431404

发布了7 篇原创文章 · 获赞 1 · 访问量 211

猜你喜欢

转载自blog.csdn.net/weixin_41442027/article/details/105558150