2.智能指针之weak_ptr

2.智能指针之weak_ptr

   一、当然在某些情况下,shared_ptr有可能造成循环引用,这样就造成了shared_ptr的引用次数一直大于0,也就不可能去释放该指针,也就造成了传说中的内存泄漏。等下,我们想想,我们起始使用智能指针的的目的就是防止内存泄漏,而循环引用又必然造成内存泄漏。说这里,我们回顾我们这一节的题目weak_ptr,对滴,为了解决该问题,我们的标准库又引入了另外一个智能指针weak_ptr。

考虑一下下面这种情况

#include <iostream>
#include <string>
#include <vector>
#include <memory>
using namespace std;
class Person {
public:
    string name;
    shared_ptr<Person> mother;
    shared_ptr<Person> father;
    vector<shared_ptr<Person>> kids;
    Person (const string& n,shared_ptr<Person> m = nullptr,shared_ptr<Person> f = nullptr)
    : name(n), mother(m), father(f) {
}
~Person()
 {
    cout << "delete " << name << endl;
}
};
shared_ptr<Person> initFamily (const string& name)
{
    shared_ptr<Person> mom(new Person(name+"’s mom"));
    shared_ptr<Person> dad(new Person(name+"’s dad"));
    shared_ptr<Person> kid(new Person(name,mom,dad));
    mom->kids.push_back(kid);
    dad->kids.push_back(kid);
    return kid;
}
int main()
{
    shared_ptr<Person> p = initFamily("nico");
    cout << "nico’s family exists" << endl;
    cout << "- nico is shared " << p.use_count() << " times" << endl;
    cout << "- name of 1st kid of nico’s mom: "
         << p->mother->kids[0]->name << endl;
    p = initFamily("jim");
    cout << "jim’s family exists" << endl;
}

在上面这个例子里,每调用一个initFamily就会生成三个对象,而其中儿子存在于父母的kids列表中,儿子中又有父母的引用,这样就造成了,无法释放该资源。

为了解决该问题,我们可以将

vector<shared_ptr<Person>> kids;更改为vector<weak_ptr<Person>> kids

这样就解决了程序中的循环引用。

weak_ptrshared_ptr管理的对象存在弱引用。如果要访问weak_ptr引用的对象,须将weak_ptr转换为shared_ptr

使用weak_ptr<T>::lock()函数则可以返回其所引用的shared_ptr

 

注意:如果使用weak_ptr时不能确保shared_ptr中的资源是否已经释放,我们可以使用以下几种方式来确保该问题的发生。

1. 使用expired(),如果该对象已经被释放,则返回true(与使用use_count一样)

2. 使用shared_ptr的构造函数将weak_ptr转换为shared_ptr,然后使用,如果已经被释放了,则weak_ptr将抛出bad_weak_ptr这个标准异常。

3. 使用use_count(),但是这个标准库指出“use_count() is not necessarily efficient.”


二、使用shared_ptr时要注意这种情况:

int* p = new int;
shared_ptr<int> sp1(p);
shared_ptr<int> sp2(p);

   在这种情况下,sp1,与sp2都能释放p的资源,当sp1释放了资源之后,sp2在次释放则会出现异常,所以通常情况下,我们new 一个对象之后,立即创建一个智能指针管理该对象。

例如:

shared_ptr<int> sp1(new int);
shared_ptr<int> sp2(sp1); // OK

三、看下面这段代码

class Person {
public:
...
    void setParentsAndTheirKids (shared_ptr<Person> m = nullptr,
    shared_ptr<Person> f = nullptr) 
    {
        mother = m;
        father = f;
        if (m != nullptr) 
        {
            m->kids.push_back(shared_ptr<Person>(this)); // ERROR
        }
        if (f != nullptr) 
        {
            f->kids.push_back(shared_ptr<Person>(this)); // ERROR
        }
    }
...
};

在上面这个例子里,不能将this转换成shared_ptr,因为这样会有两组shared_ptr共同使用这个对象,就会造成上面提到的那个为题,这的资源将会被释放两次会引起异常。

可是怎么解决这个问题呢:

1. 在这个函数声明中再添加一个shared_ptr指针作为第三个参数。

2. 利用标准库提供的std::enable_shared_from_this

使用std::enable_shared_from_this

class Person : public std::enable_shared_from_this<Person>
{
public:
...
    Person (const string& n,
    shared_ptr<Person> m = nullptr,
    shared_ptr<Person> f = nullptr)
    : name(n), mother(m), father(f)
    {
        if (m != nullptr) 
        {
            m->kids.push_back(shared_from_this()); 
        }    
        if (f != nullptr) 
        {
            f->kids.push_back(shared_from_this()); 
        }
    }
...
};

这里有一个需要注意的地方,那就是不能在构造函数中使用shared_from_this。

使用shared_from_this();函数时,该类需要从enable_shared_from_this<T>派生

四、cast 语义:

当需要转换指针时,用普通的static_cast不行,必须使用static_pointer_casrt;

例如:

auto p1 = shared_ptr<void>(new int);

auto p2 = static_pointer_cast<int*>(p1);//ok

五、共享计数器

struct A
{
    int *n;
}

std::shared_ptr<A> pA();
auto pN = std::shared_ptr<int>(pA,pA->n);
cout<<pA.use_count()<<pN.use_count(); //输出 2 2;








  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值