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())
{
    //.....  
}

猜你喜欢

转载自www.cnblogs.com/jialin0x7c9/p/12218645.html
今日推荐