shared_ptr初始化、循环引用、线程安全问题

循环引用,两个对象相互使用shared_ptr指向对方

使用shared_ptr需要注意的问题

1、不要用一个原始指针初始化多个shared_ptr,原因在于,会造成二次销毁,

如下所示:

 int *p5 = new int;
 std::shared_ptr<int> p6(p5);
 std::shared_ptr<int> p7(p5);// logic error

2、避免循环引用。智能指针最大的一个陷阱是循环引用,循环引用会导致内存泄漏。

A内部有指向B,B内部有指向A,这样对于A,B必定是在A析构后B才析构,对于B,A必定是B析构后才析构A,这就是循环引用的问题,违反常规,导致内存泄露。
A内部指向某块资源,只有等那块资源释放了,A才能析构,但是B内部也指向了A,这样都等着对方先析构

#include ciostream>
#include<nemory>
 
using namespace std;
 
class BB;
class AA{
    public:
    shared_ptr<BB> bptr;
    ~A(){cout<<"~A()"<<endl;}
}
 
class BB
{
    public:
    shared_ptr<AA> aptr;
    ~B( ){cout<<"~BB()"<<endl;}
}
int main() {
    shared_ptr<AA> aa(new AA());
    shared_ptr<BB> bb(new BB());
    aa->bptr = bb;
    bb->aptr = aa;
    return 0;
}

上面执行后并不会显示两个析构函数。

解除循环引用的方法:
1.当只剩下最后一个引用的时候需要手动打破循环引用释放对象。

2.当A的生存期超过B的生存期的时候,B改为使用一个普通指针指向A。

3.使用弱引|用的智能旨针打破这种循环引用。
在这里插入图片描述

3、线程安全问题(多线程读写shared_ptr要加锁)

假设下面这样,简化突出重点
在这里插入图片描述
如果让y=x,要涉及两个步骤

在这里插入图片描述
既然有两个步骤,那么如果没有mutex,在多线程里面就会出现竞争。
假设有 3 个 shared_ptr 对象 x、g、n:

在这里插入图片描述
线程 A 执行 x = g; (即 read g),以下完成了步骤 1,还没来及执行步骤 2
在这里插入图片描述
线程B执行g=n,并且两个步骤一起完成了
在这里插入图片描述
在这里插入图片描述因此,shared_ptr的线程不安全性在于它需要操作两个成员,即new出来的对象和计数器。在多线程下,不能保证new出来一个对象一定能被放入shared_ptr中,也不能保证智能指针管理的引用计数的正确性,这是因为shared_ptr操作不是一气呵成的。即存在以下情况:同一个shared_ptr对象可以被多线程同时读取。不同的shared_ptr对象可以被多线程同时修改。同一个shared_ptr对象不能被多线程直接修改,但可以通过原子函数完成。因此在创建一个shared_ptr时,需要使用C++11提供的make_shared模板,make_shared创建shared_ptr只申请一次内存,避免了上述错误,也提高了性能,同时在读写操作时,需要加锁。

所以多线程读写同一个 shared_ptr 必须加锁


版权声明:本文为baidu_41553551原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
THE END
< <上一篇
下一篇>>