C++智能指针

1、智能指针基础

1.1、什么是智能指针

  • 智能指针是一种用于管理动态分配对象的指针,它能够自动处理资源的生命周期,从而避免内存泄漏和悬挂指针等问题。智能指针的主要目的是简化内存管理,确保在不再需要对象时自动释放其占用的内存。

1.2、智能指针解决了什么问题

  1. 内存泄漏:内存手动释放,使用智能指针可以自动释放。
  2. 共享所有权指针的传播和释放,比如多线程使用同一个对象时析构问题。

1.3、智能指针分类

  • C++里面有四个智能指针:auto_ptr、share_ptr、unique_ptr、weak_ptr。其中后三个是C++11支持的,并且第一个已经在C++11弃用
  1. std::unique_ptr:独占式
    1. 是一种独占所有权的智能指针,它确保只有一个智能指针可以指向某个对象。当 std::unique_ptr 超出作用域时,它会自动删除其管理的对象。
    2. 通常用于管理单个对象,不允许拷贝,但可以移动。
    3. unique_ptr独占对象的所有权,由于没有引用计数,性能较好。
  2. std::shared_ptr:共享
    1. std::shared_ptr 是一种共享所有权的智能指针,允许多个指针指向同一个对象。
    2. 它通过引用计数来跟踪有多少个指针指向同一个对象,当最后一个 std::shared_ptr 被销毁时,对象才会被删除。
    3. 用于需要共享资源的情况。
    4. share_ptr共享对象的所有权,但性能略差。
  3. std::weak_ptr:弱引用
    1. std::weak_ptr 是一种弱引用智能指针,它不拥有对象的所有权,而是与 std::shared_ptr 一起使用,用于避免循环引用导致的内存泄漏。
    2. std::weak_ptr 可以通过 lock() 方法转换为 std::shared_ptr,以访问其所引用的对象。

2、shared_ptr

  1. std::shared_ptr使用引用计数,每一个shared_ptr的拷贝都指向相同的内存。在最后一个shared_ptr析构的时候,内存才会被释放。
  2. sharedd_ptr共享被管理的对象,同一时刻可以有多个shared_ptr拥有对象的所有权,当最后一个shared_ptr对象销毁时,被管理对象自动销毁。
  • 简单的说,shared_ptr实现包含了两个部分:
    • 一个指向堆上创建的对象的裸指针 raw_ptr。
    • 一个指向内部隐藏的、共享的管理对象 shared_count_object。其中use_count是当前这个堆上对象被多少对象引用了,简单来说就是引用计数。

2.1、shared_ptr内存模型

C++智能指针_第1张图片

  • shared_ptr内部包含两个指针,一个(Ptr to T)指向对象(T Object),一个(Ptr to control block)指向控制块(Control Block)。控制块包含一个引用计数(reference count)、一个弱计数(weak count)和其他数据(other data)(比如删除器、分配器等)。
  • reference count会累计对象的使用者数量。
std::shared_ptr<int> p1(new int(1));
std::shared_ptr<int> p2=p1;
  • p1和p2的内存模型如下:

C++智能指针_第2张图片

  • p1与p的Ptr to T都指向同一块内存空间,Ptr to control block 都指向同一个控制块(Control Block)
  • 因为两个共享智能指针指向同一个控制块,所以引用计数(reference count)为2
  • 当p1被释放的时候,计数(reference count)会减一,但此时指向的对象不会被释放,因为计数(reference count)不为0,当p2被释放的时候计数(reference count)再次减一,此时计数(reference count)为0,指向的对象也会被释放

2.2、shared_ptr使用场景

  1. 使用智能指针可以自动释放占用的内存。

    • // 使用shared_ptr智能指针Buffer对象分配在堆上,但能自动释放,buf分配在栈上
      shared_ptr<Buffer> buf=make_shared<Buffer>("auto free memory");
      
      // Buffer对象分配在堆上,但需要手动delete释放
      Buffer *buf2=new Buffer("free memory");
      
  2. 共享所有权指针的传播和释放。

    • C++智能指针_第3张图片

    • 同样的数据,但不同的业务处理不一样。使用shared_ptr智能指针,可以减少内存拷贝,因为有引入计数的存在,当引入计数变为 0 时才真正去释放内存。

2.3、shared_ptr的基本使用和常用函数

2.3.1、基本使用

  1. sp.get():返回shared_ptr中保存的裸指针。
    1. 谨慎使用get()的返回值,如果不清楚其危险性则永远不要调用get()函数。
    2. 不要保存get()的返回值 ,无论是保存为裸指针还是shared_ptr都是错误的。
    3. 保存为裸指针不知什么时候就会变成空悬指针,保存为shared_ptr则产生了独立指针。
    4. 不要delete sp.get()的返回值 ,会导致对一块内存delete两次的错误。
  2. sp.reset(…):重置shared_ptr。
    1. reset()不带参数时,若智能指针s是唯一指向该对象的指针,则释放,并置空。若智能指针s不是唯一指向该对象的指针,则引用计数减一,同时将s置为空。
    2. reset()带参数时,若智能指针s是唯一指向该对象的指针,则释放并指向新的对象。若智能指针s不是唯一指向该对象的指针,则引用计数减一,并指向新的对象。
  3. sp.use_count():返回shared_ptr的强引用计数
  4. sp.unique():若use_count为1返回true,否则返回false
  • #include 
    
    using namespace std;
    
    int main()
    {
    	int* p = new int(10);
    	// sp1与sp2都指向p
    	shared_ptr<int> sp1(p);
    	cout << "sp1.unique() = " << sp1.unique() << endl;  // 只有一个引用时是独占的返回true
    	shared_ptr<int> sp2 = sp1; 
    	cout << "sp1.unique() = " << sp1.unique() << endl;  // 多个引用时不是独占的返回false
    
    	// 输出引用计数
    	cout << "sp1.use_count() = " << sp1.use_count() << endl;
    
    	// sp1.get()返回shard_ptr中保存的裸指针,也就是p
    	cout << "sp1.get() = " << sp1.get() << endl;
    	cout << "p = " << p << endl;
    
    	// reset有参,若智能指针s是唯一指向该对象的指针,则释放并指向新的对象。
    	// 若智能指针s不是唯一指向该对象的指针,则引用计数减一,并指向新的对象。
    	sp2.reset(new int(20));
    	cout << "sp2.use_count() = " << sp2.use_count() << endl;
    	cout << "sp1.use_count() = " << sp1.use_count() << endl;
    	cout << "*sp2 = " << *sp2 << endl;
    
    	// reset没有参数就是重置清空指针
    	sp1.reset();
    	cout << "sp1.use_count() = " << sp1.use_count() << endl;
    
    	return 0;
    }
    
    
    // 结果
    /*
    sp1.unique() = 1
    sp1.unique() = 0
    sp1.use_count() = 2
    sp1.get() = 000002EC03B568D0
    p = 000002EC03B568D0
    sp2.use_count() = 1
    sp1.use_count() = 1
    *sp2 = 20
    sp1.use_count() = 0
    */
    

2.3.2、初始化 make_shared / reset

  • 通过构造函数、std::shared_ptr辅助函数和reset方法来初始化shared_ptr

    • std::shared_ptr<int> sp1(new int(1));
      std::shared_ptr<int> sp2=p1;
      std::shared_ptr<int> sp3;
      sp3.reset(new int(1));
      
  • 应该优先使用make_shared来构造智能指针,因为它更高效。

    • auto p1=make_shared<int>(100);
      shared_ptr<int> sp2=make_shared<int>(100);
      // 相当于
      shared_ptr<int> sp1(new int(100));
      
  • 不能将原始指针直接赋给一个智能指针。例如,下面这种方法是错误的:

    • std::shared_ptr<int> sp=new int(1);
      
  • 可以通过bool判断智能指针是否为空

    • #include 
      using namespace std;
      int main()
      {
      	std::shared_ptr<int> p1;
      	p1.reset(new int(1));
      	std::shared_ptr<int> p2 = p1;
      	// 引用计数此时应该是2
      	cout << "p2.use_count() = " << p2.use_count() << endl;
      	p1.reset();
      	cout << "p1.reset()\n";
      	// 引用计数此时应该是1
      	cout << "p2.use_count()= " << p2.use_count() << endl;
      	if (!p1) {
      		cout << "p1为空\n";
      	}
      	if (!p2) {
      		cout << "p2为空\n";
      	}
      	p2.reset();
      	cout << "p2.reset()\n";
      	cout << "p2.use_count()= " << p2.use_count() << endl;
      	if (!p2) {
      		cout << "p2为空\n";
      	}
      	return 0;
      }
      
      // 结果
      /*
      p2.use_count() = 2
      p1.reset()
      p2.use_count()= 1
      p1为空
      p2.reset()
      p2.use_count()= 0
      p2为空
      */
      
  • 指定删除器

    • 如果用shared_ptr管理非new对象或是没有析构函数的类时,应当为其传递合适的删除器。

      • #include 
        
        using namespace std;
        
        // 删除器
        void DeleteIntPtr(int* p)
        {
        	cout << "Call DeleteIntPtr" << endl;
        	delete p;
        }
        
        int main()
        {
        	shared_ptr<int> p(new int(1), DeleteIntPtr);
        	return 0;
        }
        
        // 结果
        /*
        Call DeleteIntPtr
        */
        
    • 当p的引用计数为0时,自动调用删除器DeleteIntPtr来释放对象的内存。删除器可以是一个lambda表达式,上面的写法可以改为:

      • shared_ptr<int> p(new int(1),[](int *p){
        	cout<< "Call DeleteIntPtr"<<endl;
        	delete p;
        });
        
    • 当使用shared_ptr管理动态数组时,需要指定删除器,因为shared_ptr的默认删除器不支持数据对象,代码如下:

      • std::shared_ptr<int> p(new int[10],[](int *p){delete [] p;});
        

2.4、shared_ptr使用要注意的问题

  1. 不要用一个原始指针初始化多个shared_ptr。
  • int *ptr=new int;
    shared_ptr<int> p1(ptr);
    shared_ptr<int> p2(ptr);//逻辑错误,报错
    
  1. 不要在函数实参中创建shared_ptr
  • // 错误示范
    function(shared_ptr<int>(new int),g());
    // 因为C++的函数参数的计算顺序在不同的编译器不同的约定下可能是不一样的,一般是从右到左,但也可能从左到右;
    // 所以,可能的过程是先new int,然后调用g(),如果恰好g()发生异常,而shared_ptr还没有创建,则int内存泄漏;
    // 正确的写法应该是先创建智能指针,代码如下:
    shared_ptr<int> p1(new int);
    function(p1,g());
    
  • 通过shared_from_this返回this指针

    • 不要将this指针作为shared_ptr返回回来,因为this指针本质上是一个裸指针,因此,可能会导致重复析构

    • #include 
      #include 
      
      using namespace std;
      
      class MyClass
      {
      public:
      	shared_ptr<MyClass> GetSelf() {
      		return shared_ptr<MyClass>(this);//不要这样做
      	}
      	MyClass() {
      		cout << "MyClass()" << endl;
      	};
      	~MyClass() {
      		cout << "~MyClass()" << endl;
      	};
      
      
      };
      
      int main()
      {
      	// sp1与sp2都会调用new MyClass的析构函数,一个对象析构两次
      	shared_ptr<MyClass> sp1(new MyClass);
      	shared_ptr<MyClass> sp2 = sp1->GetSelf();
      	return 0;
      }
      
    • 在这个例子中,由于用同一个指针(this)构造了两个智能指针sp1和sp2,而他们之间是没有任何关系的,在离开作用域之后this将会被构造的两个智能指针各自析构,导致重复析构的错误。

    • 正确返回this的shared_ptr的做法是:让目标类继承std::enable_shared_from_this类,然后使用基类的成员函数shared_from_this()返回this的shared_ptr,如下所示:

      • #include 
        #include 
        
        using namespace std;
        
        // 继承std::enable_shared_from_this
        class MyClass: public std::enable_shared_from_this<MyClass>
        {
        public:
        	shared_ptr<MyClass> GetSelf() {
        		return shared_from_this();//不要这样做
        	}
        	MyClass() {
        		cout << "MyClass()" << endl;
        	};
        	~MyClass() {
        		cout << "~MyClass()" << endl;
        	};
        
        
        };
        
        int main()
        {
        	shared_ptr<MyClass> sp1(new MyClass);
        	shared_ptr<MyClass> sp2 = sp1->GetSelf();
        	return 0;
        }
        
  • 避免循环引用,会导致内存泄漏。例如:

    • // 循环引用导致ap和bp的引用计数为2,在离开作用域之后,ap和bp的引用计数减为1,并不回减为0,导致两个指针都不会被析构,产生内存泄漏。
      // 解决的办法是把A和B任何一个成员变量改为weak_ptr。
      
      #include 
      #include 
      
      using namespace std;
      
      class A;
      class B;
      
      class B
      {
      public:
      	shared_ptr<A> aptr;
      	B();
      	~B();
      
      private:
      
      };
      
      B::B()
      {
      	cout << "B()" << endl;
      }
      
      B::~B()
      {
      	cout << "B is deleted" << endl;
      }
      
      class A
      {
      public:
      	shared_ptr<B> bptr;
      	A();
      	~A();
      
      private:
      
      };
      
      A::A()
      {
      	cout << "A()" << endl;
      }
      
      A::~A()
      {
      	cout << "A is deleted" << endl;
      }
      
      
      int main()
      {
      	shared_ptr<A> ap(new A);
      	shared_ptr<B> bp(new B);
      
      	ap->bptr = bp;
      	bp->aptr = ap;
      
      	cout << "main leave" << endl; // 循环引用导致ap bp退出了作用域都没有析构
      	return 0;
      }
      

3、unique_ptr

3.1、定义

  1. unique_ptr是一个独占型的智能指针,不能将其复制给另一个unique_ptr。
  2. unique_ptr可以指向一个数组。
  3. unique_ptr需要确定删除器的类型。

3.2、使用

  1. unique_ptr是一个独占型的智能指针,它不允许其他的智能指针共享其内部的指针,不允许通过赋值将一个unique_ptr赋值给另一个unique_ptr。
  • unique_ptr<int> up1 = make_unique<int>(10);
    unique_ptr<int> up2 = up1;  // 报错,独占指针不能赋值给另一个独占指针,共享指针也不行
    shared_ptr<int> sp1 = up1;  // 报错
    
  1. std::move(…)转移unique_ptr
  • unique_ptr不允许复制,但可以通过函数返回给其他的unique_ptr,还可以通过std::move来转移到其他的unique_ptr,这样它本身就不再拥有原来指针的所有权了

    • unique_ptr<T> my_ptr(new T);
      unique_ptr<T> my_ptr2=std::move(my_ptr);
      unique_ptr<T> my_ptr3=my_ptr2;//报错,不能复制
      
  1. make_unique初始化
  • std::make_shared是c++11的一部分,但std::make_unique不是。它是在c++14里加入标准库的。

    • auto upw1(std::make_unique<T>());// with make func
      std::unique_ptr<T> upw2(new T);  // without make func
      
  1. unique_ptr与shared_ptr的区别
  • unique_ptr可以指向一个数组。

    • std::unique_ptr<int []> ptr(new int[10]);
      ptr[9]=9;
      std::shared_ptr<int []> ptr2(new int[10]);//这是不合法的
      
  1. unique_ptr指定删除器和shared_ptr有区别。
  • std::shared_ptr<int> ptr3(new int(1), [](int *p){delete p;}); // 正确 
    std::unique_ptr<int> ptr4(new int(1), [](int *p){delete p;}); // 错误
    
  1. unique_ptr需要确定删除器的类型,所以不能像shared_ptr那样直接指定删除器,可以这样写:
  • std::unique_ptr<int,void(*)(int *)> ptr(new int(1),[](int *p){delete p;});
    

7.智能指针的选择

  • 关于shared_ptr和unique_ptr的使用场景是要根据实际应用需求来选择。
  • 如果希望只有一个智能指针管理资源或者管理数组就用unique_ptr,如果希望多个智能指针管理同一个资源就用shared_ptr。

4、weak_ptr

  • share_ptr虽然已经很好用了,但是有一点share_ptr智能指针还是有内存泄露的情况,当两个对象相互使用一个shared_ptr成员变量指向对方,会造成循环引用,使引用计数失效,从而导致内存泄漏。

  • weak_ptr 是一种不控制对象生命周期的智能指针, 它指向一个 shared_ptr 管理的对象. 进行该对象的内存管理的是那个强引用的shared_ptr, weak_ptr只是提供了对管理对象的一个访问手段。

  • weak_ptr 设计的目的是为配合 shared_ptr 而引入的一种智能指针来协助 shared_ptr 工作, 它只可以从一个 shared_ptr 或另一个 weak_ptr 对象构造, 它的构造和析构不会引起引用记数的增加或减少。

4.1、weak_ptr的基本用法

  1. 通过use_count()方法获取当前观察资源的引用计数。
  • 	shared_ptr<int> sp(new int(10));
    	weak_ptr<int> wp1(sp);
    	// 或者
    	weak_ptr<int> wp2;
    	wp2 = sp;
    
    	cout << wp1.use_count() << endl; //结果为输出1
    	cout << wp2.use_count() << endl; //结果为输出1
    
  1. 通过expired()方法判断所观察资源是否已经释放。
  • shared_ptr<int> sp(new int(10));
    	weak_ptr<int> wp(sp);
    
    	if (wp.expired())
    		cout << "weak_ptr无效,资源已释放" << endl;
    	else
    		cout << "weak_ptr有效" << endl;
    
    	sp.reset();
    
    	if (wp.expired())
    		cout << "weak_ptr无效,资源已释放" << endl;
    	else
    		cout << "weak_ptr有效" << endl;
    
    // 输出结果
    /*
    weak_ptr有效
    weak_ptr无效,资源已释放
    */
    
  1. 通过lock方法获取监视的shared_ptr。
  • 使用lock将资源锁住,lock会将weap_ptr转为shared_ptr,即使weap_ptr指向的shared_ptr资源被释放也不影响使用。

  • 如果要访问weap_ptr指向的数据,必须使用lock将weap_ptr转为shared_ptr才能访问到。

  • 在多线程中,要防止一个线程在使用智能指针,而另一个线程删除指针指针问题,可以使用weak_ptr的lock()方法。

  • auto sp = std::make_shared<int>(42); // 创建一个共享指针;
    std::weak_ptr<int> wp(sp);
    
    shared_ptr<int> p;
    if (!wp.expired())
    {
        p = wp.lock();  // 如果要取到weak_ptr中的,需要先使用lock将weak_ptr转为shard_ptr才能取值;
        sp.reset();
        cout << *p << endl;              // 42
        cout << p.use_count() << endl;   // 1
        cout << sp.use_count() << endl;  // 0
    }
    
  1. weak_ptr返回this指针
  • shared_ptr中提到不能直接将this指针返回shared_ptr,需要通过派生std::enable_shared_from_this类,并通过其方法shared_from_this来返回指针;原因是std::enable_shared_from_this类中有一个weak_ptr,这个weak_ptr用来观察this智能指针,调用shared_from_this()方法是,会调用内部这个weak_ptr的lock()方法,将所观察的shared_ptr返回。

  • #include 
    
    using namespace std;
    
    class MyClass : public std::enable_shared_from_this<MyClass>
    {
    public:
    	shared_ptr<MyClass> GetSelf() {
    		//return shared_ptr(this);  直接返回this的共享智能指针,如果直接返回MyClass会被析构两次
    		return shared_from_this();
    	}
    	MyClass() {
    		cout << "MyClass()" << endl;
    	};
    	~MyClass() {
    		cout << "~MyClass()" << endl;
    	};
    
    
    };
    
    int main()
    {
    	shared_ptr<MyClass> sp1(new MyClass);
    	shared_ptr<MyClass> sp2 = sp1->GetSelf();
    	return 0;
    }
    
  • 在外面创建MyClass对象的智能指针和通过对象返回this的智能指针都是安全的,因为shared_from_this()是内部的weak_ptr调用lock()方法之后返回的智能指针,在离开作用域之后,sp2的引用计数减为0,A对象会被析构,不会出现A对象被析构两次的问题。

  • 需要注意的是,获取自身智能指针的函数仅在shared_ptr的构造函数被调用之后才能使用,因为enable_shared_from_this内部的weak_ptr只有通过shared_ptr才能构造。

  1. weak_ptr解决循环引用问题
  • 在shared_ptr提到智能指针循环引用的问题,因为智能指针的循环引用会导致内存泄漏,可以通过weak_ptr解决该问题,只要将A或B的任意一个成员变量改为weak_ptr。

  • 如果不使用weak_ptr,循环引用导致ap和bp的引用计数为2,在离开作用域之后,ap和bp的引用计数减为1,并不回减为0,导致两个指针都不会被析构,产生内存泄漏。

  • 这样在对B的成员赋值时,即执行bp->aptr=ap;时,由于aptr是weak_ptr,它并不会增加引用计数,所以ap的引用计数仍然会是1,在离开作用域之后,ap的引用计数为减为0,A指针会被析构,析构后其内部的bptr的引用计数会被减为1,然后在离开作用域后bp引用计数又从1减为0,B对象也被析构,不会发生内存泄漏。

  • #include 
    using namespace std;
    
    class A;
    class B;
    
    class B
    {
    public:
    	shared_ptr<A> aptr;
    	B()
    	{
    		cout << "B()" << endl;
    	}
    
    	~B()
    	{
    		cout << "B is deleted" << endl;
    	}
    };
    
    class A
    {
    public:
    	weak_ptr<B> bptr;
    	//shared_ptr bptr; 错误
    	A()
    	{
    		cout << "A()" << endl;
    	}
    	~A()
    	{
    		cout << "A is deleted" << endl;
    	}
    };
    
    
    int main()
    {
    	shared_ptr<A> ap(new A);
    	shared_ptr<B> bp(new B);
    
    	ap->bptr = bp;
    	bp->aptr = ap;
    
    	cout << "ap.use_count() = " << ap.use_count() << endl;  // 2
    	cout << "bp.use_count() = " << bp.use_count() << endl;  // 1
    	return 0;
    }
    
    // 结果
    /*
    A()
    B()
    ap.use_count() = 2
    bp.use_count() = 1
    B is deleted
    A is deleted
    */
    
  1. weak_ptr使用注意事项
  • weak_ptr在使用前需要检查合法性。

  • weak_ptr<int> wp;
    {
    	shared_ptr<int> sp(new int(1));
    	wp=sp;
    	shared_ptr<int> sp_ok = wp.lock();//wp没有重载->操作符。只能这样取所指向的对象
    }
    
    shared_ptr<int> sp_null=wp.lock();
    
  • 因为上述代码中sp和sp_ok离开了作用域,其容纳的K对象已经被释放了。得到了一个容纳NULL指针的sp_null对象。在使用wp前需要调用wp.expired()函数判断一下。

  • 因为wp还仍旧存在,虽然引用计数等于0,仍有某处“全局”性的存储块保存着这个计数信息。直到最后一个weak_ptr对象被析构,这块“堆”存储块才能被回收。否则weak_ptr无法指到自己所容纳的那个指针资源的当前状态。

5、智能指针安全性问题

  • 引用计数本身是安全的,至于智能指针是否安全需要结合实际使用分情况讨论。
  1. 多线程代码操作的是同一个shared_ptr的对象,此时是不安全的。
    比如std::thread的回调函数,是一个lambda表达式,其中引用捕获一个shared_ptr:
  • std::thread td([&sp1](){...});
    
  • 又或者通过回调函数的参数传入的shared_ptr对象,参数类型引用:

  • void fun(shared_ptr<A> &sp)
    {
    // ...
    }
    // ...
    
    std::thread td(fun,sp1);
    
  • 这个时候必然不是线程安全的。

  1. 多线程代码操作的不是同一个shared_ptr的对象
    这里指的是管理的数据是同一份,而shared_ptr不是同一个对象。 比如多线程回调的lambda的是按值捕获的对象。
  • std::thread([sp1](){...});
    
  • 另个线程传递的shared_ptr是值传递,而非引用:

  • void fn(shared_ptr<A>sp) { 
    ... 
    }
    ..
    std::thread td(fn, sp1);
    
  • 这时候每个线程内看到的sp,他们所管理的是同一份数据,用的是同一个引用计数。但是各自是不同的对象,当发生多线程中修改sp指向的操作的时候,是不会出现非预期的异常行为的。

  • 也就是说,如下操作是安全的:

  • void fun(shared_ptr<A> sp)
    {
    // ...
    	if(...){
    		sp=other_sp;
    	}
    	else {
    		sp=other_sp2;
    	}
    }
    
  • 需要注意:所管理数据的线程安全性问题。显而易见,所管理的对象必然不是线程安全的,必然 sp1、sp2、sp3智能指针实际都是指向对象A, 三个线程同时操作对象A,那对象的数据安全必然是需要对象A自己去保证。

6、总结

  • 智能指针之间不能混用。
  1. weak_ptr要和shared_ptr搭配使用,不能单独使用weak_ptr。
  2. weak_ptr的lock()使用是要先调用lock()再调用expired()。调用lock()之后use_count+1。

据是同一份,而shared_ptr不是同一个对象。** 比如多线程回调的lambda的是按值捕获的对象。

  • std::thread([sp1](){...});
    
  • 另个线程传递的shared_ptr是值传递,而非引用:

  • void fn(shared_ptr<A>sp) { 
    ... 
    }
    ..
    std::thread td(fn, sp1);
    
  • 这时候每个线程内看到的sp,他们所管理的是同一份数据,用的是同一个引用计数。但是各自是不同的对象,当发生多线程中修改sp指向的操作的时候,是不会出现非预期的异常行为的。

  • 也就是说,如下操作是安全的:

  • void fun(shared_ptr<A> sp)
    {
    // ...
    	if(...){
    		sp=other_sp;
    	}
    	else {
    		sp=other_sp2;
    	}
    }
    
  • 需要注意:所管理数据的线程安全性问题。显而易见,所管理的对象必然不是线程安全的,必然 sp1、sp2、sp3智能指针实际都是指向对象A, 三个线程同时操作对象A,那对象的数据安全必然是需要对象A自己去保证。

6、总结

  • 智能指针之间不能混用。
  1. weak_ptr要和shared_ptr搭配使用,不能单独使用weak_ptr。
  2. weak_ptr的lock()使用是要先调用lock()再调用expired()。调用lock()之后use_count+1。

你可能感兴趣的:(c++,开发语言)