• shared_ptr和weak_ptr以及循环引用问题


    shared_ptr的出现:

      使用裸指针对堆对象(动态内存)进行管理是极其容易出现问题的。例如:忘记释放内存造成的内存泄漏、尚有指针引用内存的情况下释放了该内存等等的问题。

      为此能够更加智能地保留或者释放堆(动态)对象,标准库以及boost库提供了智能指针。智能指针负责自动释放所指向的对象。智能指针的使用和普通指针类似,解引用一个智能指针返回它指的对象。

      shared_ptr:允许多个指针指向同一个对象。

    shared_ptr用法:

    #include <iostream>
    #include <memory>
    
    using namespace std;
    
    class Test
    {
    public:
        Test(string s):_str(s)
        {
            cout << "Test create" << endl;
        }
    
        ~Test()
        {
            cout << "Test delete" << endl;
        }
        string &getStr()
        {
            return _str;
        }
        void setStr(string s)
        {                                                                                                                                        
            _str = s;
        }
    
        void print()
        {
            cout << _str << endl;
        }
    
    private:
        string _str;
    };
    int main()
    {
        shared_ptr<Test> p1 = make_shared<Test>("pTest1");
        shared_ptr<Test> p2 = make_shared<Test>("pTest2");
        shared_ptr<Test> p3 = make_shared<Test>("pTest3");
        p3 = p1;
        p2 = p1;
        cout << p3.use_count() << endl;
        cout << p1.use_count() << endl;
        return 0;    
    }

    将p1智能指针赋值给了p3背后发生的事情:p3所指向的对象引用计数-1后为0,释放p3所指向的对象。p1的引用计数+1。

    问题:通过p3可以拿到所指向的引用计数值为3,通过p1也能拿到所指向的引用计数为3。是否p1智能指针对象和p3智能指针对象都保存了一份引用计数值呢?

    参考《c++ primer第五版》P402页所给出的解释:

    到底是用一个计数器还是其他数据结构来记录有多少指针共享对象, 完全由标准库的具体实现来决定。关键是智能指针类能记录有多少个shared_ptr指向相同的对象,并能在恰当的时候自动释放对象。

    虽然智能指针能自动释放内存,但是使用不当同样会导致内存泄漏。

    shared_ptr的循环引用问题:

    循环引用问题模型:

    问题描述:

    如果有一个类A和类B,其数据成员是一个shared_ptr指向彼此。那么此时类A和类B的引用计数ref为1。如果此时又有两个智能指针分别指向A和B,那么此时类A和类B的引用计数为2,当这两个智能指针离开其作用域的时候ref减为1,但并不会释放智能指针所指向的对象。会造成内存泄漏。

    #include <iostream>
    #include <memory>
    using namespace std;
    
    class B;
    class A
    {
    public:
        ~A()
        {
            cout << "A delete" << endl;
        }
        shared_ptr<B> ptr;
    };
    
    class B
    {
    public:
        ~B()
        {
            cout << "B delete" << endl;
        }
        shared_ptr<A> ptr;
    };
    
    int main()
    {
        while(1)
        {
            shared_ptr<A> pa(new A());   //A对象的引用计数ref=1
            shared_ptr<B> pb(new B());   //B对象的引用计数ref=1
            pa->ptr = pb;    //B对象的引用计数ref=2
            pb->ptr = pa;    //A对象的引用计数ref=2
        }
         //离开作用域后,虽然pa和pb智能指针对象释放了,但由于其所指对象的引用计数为1而未被释放,故造成内存泄漏。
    }  

    weak_ptr的出现:

      为了解决循环引用的问题,出现了弱引用的weak_ptr。weak_ptr指向对象并不会对引用计数+1。weak_ptr不对其所指的对象进行内存资源的管理。解决循环引用的方法就是将shared_ptr的数据成员改为weak_ptr。

    weak_ptr的用法:

      当创建一个weak_ptr时,要用一个shared_ptr来初始化它:

    shared_ptr<int> p = make_shared<int>(111);
    weak_ptr wp(p);

      因为是弱引用,创建wp不会改变p的引用计数。有可能weak_ptr所指向的对象不存在了,因此无法直接通过weak_ptr指针访问其所指向的对象,应该通过调用lock()方法将weak_ptr提升为一个shared_ptr,再访问其所指向的对象。如果提升失败那么指向的对象已被释放。

    if (shared_ptr<int> p = wp.lock())
    {
        //.....  
    }
  • 相关阅读:
    Redis初启(一)
    分布式架构下的通信(一)
    Python学习笔记:本月、上月、次月生成
    Python学习笔记:字符串转换为时间对象
    超级详细的mysql数据库安装指南
    net core 3.1 mvc string接收为null解决方案
    linux 连接跟踪 conntrack
    Cilium 数据链路
    strace/perf trace 使用
    quagga 部署启动BGP
  • 原文地址:https://www.cnblogs.com/jialin0x7c9/p/12218645.html
Copyright © 2020-2023  润新知