zoukankan      html  css  js  c++  java
  • 【C++11新特性】 C++11智能指针之weak_ptr

    如题,我们今天要讲的是C++11引入的三种智能指针中的最后一个:weak_ptr。在学习weak_ptr之前最好对shared_ptr有所了解。如果你还不知道shared_ptr是何物,可以看看我的另一篇文章【C++11新特性】 C++11智能指针之shared_ptr。

    1、为什么需要weak_ptr?
    在正式介绍weak_ptr之前,我们先来回忆一下shared_ptr的一些知识。我们知道shared_ptr是采用引用计数的智能指针,多个shared_ptr实例可以指向同一个动态对象,并维护了一个共享的引用计数器。对于引用计数法实现的计数,总是避免不了循环引用(或环形引用)的问题,shared_ptr也不例外。

    我们先来看看下面这个例子:

    #include <iostream>
    #include <memory>
    #include <vector>
    using namespace std;

    class ClassB;

    class ClassA
    {
    public:
    ClassA() { cout << "ClassA Constructor..." << endl; }
    ~ClassA() { cout << "ClassA Destructor..." << endl; }
    shared_ptr<ClassB> pb; // 在A中引用B
    };

    class ClassB
    {
    public:
    ClassB() { cout << "ClassB Constructor..." << endl; }
    ~ClassB() { cout << "ClassB Destructor..." << endl; }
    shared_ptr<ClassA> pa; // 在B中引用A
    };

    int main() {
    shared_ptr<ClassA> spa = make_shared<ClassA>();
    shared_ptr<ClassB> spb = make_shared<ClassB>();
    spa->pb = spb;
    spb->pa = spa;
    // 函数结束,思考一下:spa和spb会释放资源么?
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    上面代码的输出如下:

    ClassA Constructor...
    ClassB Constructor...
    Program ended with exit code: 0
    1
    2
    3
    从上面代码中,ClassA和ClassB间存在着循环引用,从运行结果中我们可以看到:当main函数运行结束后,spa和spb管理的动态资源并没有得到释放,产生了内存泄露。

    为了解决类似这样的问题,C++11引入了weak_ptr,来打破这种循环引用。

    2、weak_ptr是什么?
    weak_ptr是为了配合shared_ptr而引入的一种智能指针,它指向一个由shared_ptr管理的对象而不影响所指对象的生命周期,也就是将一个weak_ptr绑定到一个shared_ptr不会改变shared_ptr的引用计数。不论是否有weak_ptr指向,一旦最后一个指向对象的shared_ptr被销毁,对象就会被释放。从这个角度看,weak_ptr更像是shared_ptr的一个助手而不是智能指针。

    3、weak_ptr如何使用?
    接下来,我们来看看weak_ptr的简单用法。

    3.1如何创建weak_ptr实例
    当我们创建一个weak_ptr时,需要用一个shared_ptr实例来初始化weak_ptr,由于是弱共享,weak_ptr的创建并不会影响shared_ptr的引用计数值。

    示例:

    int main() {
    shared_ptr<int> sp(new int(5));
    cout << "创建前sp的引用计数:" << sp.use_count() << endl; // use_count = 1

    weak_ptr<int> wp(sp);
    cout << "创建后sp的引用计数:" << sp.use_count() << endl; // use_count = 1
    }
    1
    2
    3
    4
    5
    6
    7
    3.2如何判断weak_ptr指向对象是否存在
    既然weak_ptr并不改变其所共享的shared_ptr实例的引用计数,那就可能存在weak_ptr指向的对象被释放掉这种情况。这时,我们就不能使用weak_ptr直接访问对象。那么我们如何判断weak_ptr指向对象是否存在呢?C++中提供了lock函数来实现该功能。如果对象存在,lock()函数返回一个指向共享对象的shared_ptr,否则返回一个空shared_ptr。

    示例:

    class A
    {
    public:
    A() : a(3) { cout << "A Constructor..." << endl; }
    ~A() { cout << "A Destructor..." << endl; }

    int a;
    };

    int main() {
    shared_ptr<A> sp(new A());
    weak_ptr<A> wp(sp);
    //sp.reset();

    if (shared_ptr<A> pa = wp.lock())
    {
    cout << pa->a << endl;
    }
    else
    {
    cout << "wp指向对象为空" << endl;
    }
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    试试把sp.reset()这行的注释去掉看看结果有什么不同。

    除此之外,weak_ptr还提供了expired()函数来判断所指对象是否已经被销毁。

    示例:

    class A
    {
    public:
    A() : a(3) { cout << "A Constructor..." << endl; }
    ~A() { cout << "A Destructor..." << endl; }

    int a;
    };

    int main() {
    shared_ptr<A> sp(new A());
    weak_ptr<A> wp(sp);
    sp.reset(); // 此时sp被销毁
    cout << wp.expired() << endl; // true表示已被销毁,否则为false
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    代码输入如下:

    A Constructor...
    A Destructor...
    1
    1
    2
    3
    3.3如何使用weak_ptr
    weak_ptr并没有重载operator->和operator *操作符,因此不可直接通过weak_ptr使用对象,典型的用法是调用其lock函数来获得shared_ptr示例,进而访问原始对象。

    最后,我们来看看如何使用weak_ptr来改造最前面的代码,打破循环引用问题。

    class ClassB;

    class ClassA
    {
    public:
    ClassA() { cout << "ClassA Constructor..." << endl; }
    ~ClassA() { cout << "ClassA Destructor..." << endl; }
    weak_ptr<ClassB> pb; // 在A中引用B
    };

    class ClassB
    {
    public:
    ClassB() { cout << "ClassB Constructor..." << endl; }
    ~ClassB() { cout << "ClassB Destructor..." << endl; }
    weak_ptr<ClassA> pa; // 在B中引用A
    };

    int main() {
    shared_ptr<ClassA> spa = make_shared<ClassA>();
    shared_ptr<ClassB> spb = make_shared<ClassB>();
    spa->pb = spb;
    spb->pa = spa;
    // 函数结束,思考一下:spa和spb会释放资源么?
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    输出结果如下:

    ClassA Constructor...
    ClassB Constructor...
    ClassA Destructor...
    ClassB Destructor...
    Program ended with exit code: 0
    1
    2
    3
    4
    5
    从运行结果可以看到spa和spb指向的对象都得到释放!
    ————————————————
    版权声明:本文为CSDN博主「Fred^_^」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/Xiejingfa/article/details/50772571

  • 相关阅读:
    c:forTokens标签循环输出
    jsp转long类型为date,并且格式化
    spring中@Param和mybatis中@Param使用区别(暂时还没接触)
    734. Sentence Similarity 有字典数组的相似句子
    246. Strobogrammatic Number 上下对称的数字
    720. Longest Word in Dictionary 能连续拼接出来的最长单词
    599. Minimum Index Sum of Two Lists两个餐厅列表的索引和最小
    594. Longest Harmonious Subsequence强制差距为1的最长连续
    645. Set Mismatch挑出不匹配的元素和应该真正存在的元素
    409. Longest Palindrome 最长对称串
  • 原文地址:https://www.cnblogs.com/yiyi20120822/p/11612764.html
Copyright © 2011-2022 走看看