• C++11——弱引用智能指针


    链接: https://subingwen.cn/cpp/weak_ptr/#3-%E8%A7%A3%E5%86%B3%E5%BE%AA%E7%8E%AF%E5%BC%95%E7%94%A8%E9%97%AE%E9%A2%98

    在 C++ 中没有垃圾回收机制,必须自己释放分配的内存,否则就会造成内存泄露。解决这个问题最有效的方法是使用智能指针(smart pointer)。智能指针是存储指向动态分配(堆)对象指针的类,用于生存期的控制,能够确保在离开指针所在作用域时,自动地销毁动态分配的对象,防止内存泄露。智能指针的核心实现技术是引用计数,每使用它一次,内部引用计数加 1,每析构一次内部的引用计数减 1,减为 0 时,删除所指向的堆内存。

    C++11 中提供了三种智能指针,使用这些智能指针时需要引用头文件 :

    std::shared_ptr:共享的智能指针
    std::unique_ptr:独占的智能指针
    std::weak_ptr:弱引用的智能指针,它不共享指针,不能操作资源,是用来监视 shared_ptr 的

    1. 基本使用方法
    弱引用智能指针 std::weak_ptr 可以看做是 shared_ptr 的助手,它不管理 shared_ptr 内部的指针std::weak_ptr 没有重载操作符 * 和 ->,因为它不共享指针,不能操作资源,所以它的构造不会增加引用计数,析构也不会减少引用计数,它的主要作用就是作为一个旁观者监视 shared_ptr 中管理的资源是否存在。

    1.1 初始化

    // 默认构造函数
    constexpr weak_ptr() noexcept;
    // 拷贝构造
    weak_ptr (const weak_ptr& x) noexcept;
    template <class U> weak_ptr (const weak_ptr<U>& x) noexcept;
    // 通过shared_ptr对象构造
    template <class U> weak_ptr (const shared_ptr<U>& x) noexcept;

    在 C++11 中,weak_ptr 的初始化可以通过以上提供的构造函数来完成初始化,具体使用方法如下:

    #include <iostream>
    #include <memory>
    using namespace std;
    
    int main()
    {
        shared_ptr<int> sp(new int);
        
        weak_ptr<int> wp1;
        weak_ptr<int> wp2(wp1);
        weak_ptr<int> wp3(sp);
        weak_ptr<int> wp4;
        wp4 = sp;
        weak_ptr<int> wp5;
        wp5 = wp3;
        
        return 0;
    }
    weak_ptr<int> wp1; 构造了一个空 weak_ptr 对象
    weak_ptr<int> wp2(wp1); 通过一个空 weak_ptr 对象构造了另一个空 weak_ptr 对象
    weak_ptr<int> wp3(sp); 通过一个 shared_ptr 对象构造了一个可用的 weak_ptr 实例对象
    wp4 = sp; 通过一个 shared_ptr 对象构造了一个可用的 weak_ptr 实例对象(这是一个隐式类型转换)
    wp5 = wp3; 通过一个 weak_ptr 对象构造了一个可用的 weak_ptr 实例对象


    1.2 其他常用方法
    1.2.1 use_count()
    通过调用 std::weak_ptr 类提供的 use_count() 方法可以获得当前所观测资源的引用计数,函数原型如下:

    // 函数返回所监测的资源的引用计数
    long int use_count() const noexcept;

    修改一下上面的测试程序,添加打印资源引用计数的代码:

    #include <iostream>
    #include <memory>
    using namespace std;
    
    int main()
    {
        shared_ptr<int> sp(new int);
        
        weak_ptr<int> wp1;
        weak_ptr<int> wp2(wp1);
        weak_ptr<int> wp3(sp);
        weak_ptr<int> wp4;
        wp4 = sp;
        weak_ptr<int> wp5;
        wp5 = wp3;
        
        cout << "use_count: " << endl;
        cout << "wp1: " << wp1.use_count() << endl;
        cout << "wp2: " << wp2.use_count() << endl;
        cout << "wp3: " << wp3.use_count() << endl;
        cout << "wp4: " << wp4.use_count() << endl;
        cout << "wp5: " << wp5.use_count() << endl;
        return 0;
    }

    测试程序输出的结果为:

    use_count:
    wp1: 0
    wp2: 0
    wp3: 1
    wp4: 1
    wp5: 1

    通过打印的结果可以知道,虽然弱引用智能指针 wp3、wp4、wp5 监测的资源是同一个,但是它的引用计数并没有发生任何的变化,也进一步证明了 weak_ptr只是监测资源,并不管理资源。

    1.2.3 expired()
    通过调用 std::weak_ptr 类提供的 expired() 方法来判断观测的资源是否已经被释放,函数原型如下:

    // 返回true表示资源已经被释放, 返回false表示资源没有被释放
    bool expired() const noexcept;


    函数的使用方法如下:

    #include <iostream>
    #include <memory>
    using namespace std;
    
    int main()
    {
        shared_ptr<int> shared(new int(10));
        weak_ptr<int> weak(shared);
        cout << "1. weak " << (weak.expired() ? "is" : "is not") << " expired" << endl;
        
        shared.reset();
        cout << "2. weak " << (weak.expired() ? "is" : "is not") << " expired" << endl;
        
        return 0;
    }

    测试代码输出的结果:

    1. weak is not expired
    2. weak is expired

    weak_ptr 监测的就是 shared_ptr 管理的资源,当共享智能指针调用 shared.reset(); 之后管理的资源被释放,因此 weak.expired() 函数的结果返回 true,表示监测的资源已经不存在了。

    1.2.3 lock()
    通过调用 std::weak_ptr 类提供的 lock() 方法来获取管理所监测资源的 shared_ptr 对象,函数原型如下:

    shared_ptr<element_type> lock() const noexcept;

    函数的使用方法如下:

    #include <iostream>
    #include <memory>
    using namespace std;
    
    int main()
    {
        shared_ptr<int> sp1, sp2;
        weak_ptr<int> wp;
        
        sp1 = std::make_shared<int>(520);
        wp = sp1;
        sp2 = wp.lock();
        cout << "use_count: " << wp.use_count() << endl;
        
        sp1.reset();
        cout << "use_count: " << wp.use_count() << endl;
        
        sp1 = wp.lock();
        cout << "use_count: " << wp.use_count() << endl;
        
        cout << "*sp1: " << *sp1 << endl;
        cout << "*sp2: " << *sp2 << endl;
        
        return 0;
    }

    测试代码输出的结果为:

    use_count: 2
    use_count: 1
    use_count: 2
    *sp1: 520
    *sp2: 520

    sp2 = wp.lock(); 通过调用 lock() 方法得到一个用于管理 weak_ptr 对象所监测的资源的共享智能指针对象使用这个对象初始化 sp2,此时所监测资源的引用计数为 2
    sp1.reset(); 共享智能指针 sp1 被重置,weak_ptr 对象所监测的资源的引用计数减 1
    sp1 = wp.lock();sp1 重新被初始化,并且管理的还是 weak_ptr 对象所监测的资源,因此引用计数加 1
    共享智能指针对象 sp1 和 sp2 管理的是同一块内存,因此最终打印的内存中的结果是相同的,都是 520
    1.2.4 reset()
    通过调用 std::weak_ptr 类提供的 reset() 方法来清空对象,使其不监测任何资源,函数原型如下:

    void reset() noexcept;

    函数的使用是非常简单的,示例代码如下:

    #include <iostream>
    #include <memory>
    using namespace std;
    
    int main()
    {
        shared_ptr<int> sp(new int(10));
        weak_ptr<int> wp(sp);
        cout << "1. wp " << (wp.expired() ? "is" : "is not") << " expired" << endl;
        
        wp.reset();
        cout << "2. wp " << (wp.expired() ? "is" : "is not") << " expired" << endl;
        
        return 0;
    }

    测试代码输出的结果为:

    1. wp is not expired
    2. wp is expired

    weak_ptr 对象 sp 被重置之后 wp.reset(); 变成了空对象,不再监测任何资源,因此 wp.expired() 返回 true

    不能使用一个原始地址初始化多个共享智能指针

    函数不能返回管理了this的共享智能指针

    共享智能指针不能循坏引用

    2. 返回管理 this 的 shared_ptr
    如果在一个类中编写了一个函数,通过这个得到管理当前对象的共享智能指针,我们可能会写出如下代码:

    #include <iostream>
    #include <memory>
    using namespace std;
    
    struct Test
    {
        shared_ptr<Test> getSharedPtr()
        {
            return shared_ptr<Test>(this);
        }
        
        ~Test()
        {
            cout << "class Test is disstruct ..." << endl;
        }
    
    };
    
    int main()
    {
        shared_ptr<Test> sp1(new Test);
        cout << "use_count: " << sp1.use_count() << endl;
        shared_ptr<Test> sp2 = sp1->getSharedPtr();
        cout << "use_count: " << sp1.use_count() << endl;
        return 0;
    }

    执行上面的测试代码,运行中会出现异常,在终端还是能看到对应的日志输出:

    use_count: 1
    use_count: 1
    class Test is disstruct ...
    class Test is disstruct ...

    通过输出的结果可以看到一个对象被析构了两次,其原因是这样的:在这个例子中使用同一个指针 this 构造了两个智能指针对象 sp1 和 sp2,这二者之间是没有任何关系的,因为 sp2 并不是通过 sp1 初始化得到的实例对象。在离开作用域之后 this 将被构造的两个智能指针各自析构,导致重复析构的错误。

    这个问题可以通过 weak_ptr 来解决,通过 wek_ptr 返回管理 this 资源的共享智能指针对象 shared_ptr。C++11 中为我们提供了一个模板类叫做 std::enable_shared_from_this<T>,这个类中有一个方法叫做 shared_from_this(),通过这个方法可以返回一个共享智能指针在函数的内部就是使用 weak_ptr 来监测 this 对象,并通过调用 weak_ptr 的 lock() 方法返回一个 shared_ptr 对象。

    修改之后的代码为:

    #include <iostream>
    #include <memory>
    using namespace std;
    
    struct Test : public enable_shared_from_this<Test>
    {
        shared_ptr<Test> getSharedPtr()
        {
            return shared_from_this();
        }
        ~Test()
        {
            cout << "class Test is disstruct ..." << endl;
        }
    };
    
    int main()
    {
        shared_ptr<Test> sp1(new Test);
        cout << "use_count: " << sp1.use_count() << endl;
        shared_ptr<Test> sp2 = sp1->getSharedPtr();
        cout << "use_count: " << sp1.use_count() << endl;
        return 0;
    }

    测试代码输出的结果为:

    use_count: 1
    use_count: 2
    class Test is disstruct ...


    最后需要强调一个细节:在调用 enable_shared_from_this 类的 shared_from_this () 方法之前必须要先初始化函数内部 weak_ptr 对象否则该函数无法返回一个有效的 shared_ptr 对象(具体处理方法可以参考上面的示例代码)。

     shared_ptr<Test> sp1(new Test);

    3. 解决循环引用问题
    智能指针如果循环引用会导致内存泄露,比如下面的例子:

    #include <iostream>
    #include <memory>
    using namespace std;
    
    struct TA;
    struct TB;
    
    struct TA
    {
        weak_ptr<TB> bptr;
        ~TA()
        {
        cout << "class TA is disstruct ..." << endl;
        }
    };
    
    struct TB
    {
        shared_ptr<TA> aptr;
        ~TB()
        {
        cout << "class TB is disstruct ..." << endl;
        }
    };
    
    void testPtr()
    {
        shared_ptr<TA> ap(new TA);
        shared_ptr<TB> bp(new TB);
        cout << "TA object use_count: " << ap.use_count() << endl;
        cout << "TB object use_count: " << bp.use_count() << endl;
        
        ap->bptr = bp;
        bp->aptr = ap;
        cout << "TA object use_count: " << ap.use_count() << endl;
        cout << "TB object use_count: " << bp.use_count() << endl;
    }
    
    int main()
    {
        testPtr();
        return 0;
    }

    程序输出的结果:

    TA object use_count: 1
    TB object use_count: 1
    TA object use_count: 2
    TB object use_count: 1
    class TB is disstruct ...
    class TA is disstruct ...


    通过输出的结果可以看到类 TA 或者 TB 的对象被成功析构了。

    上面程序中,在对类 TA 成员赋值时 ap->bptr = bp; 由于 bptr 是 weak_ptr 类型,这个赋值操作并不会增加引用计数所以 bp 的引用计数仍然为 1,在离开作用域之后 bp 的引用计数减为 0,类 TB 的实例对象被析构

    在类 TB 的实例对象被析构的时候,内部的 aptr 也被析构,其对 TA 对象的管理解除,内存的引用计数减为 1,当共享智能指针 ap 离开作用域之后,对 TA 对象的管理也解除了,内存的引用计数减为 0,类 TA 的实例对象被析构

  • 相关阅读:
    在线文件转换工具
    中鸣机器人官网
    下载tortoisegit
    知晓云,不用申请服务器即可完成小程序后台开发和部署
    全网音乐免费下载工具
    MySQL5.7 主从复制配置
    Nginx-------Nginx的安装和多域名配置
    如何获取域名(网址)对应的IP地址
    LOGO免费在线设计
    Jmeter-----保存到响应文件
  • 原文地址:https://www.cnblogs.com/Galesaur-wcy/p/15374274.html
Copyright © 2020-2023  润新知