C++ 智能指针【学习笔记】

  • 头文件memory

shared_ptr类

  • 允许多个指针指向同一个对象;

创建和初始化

shared_ptr<string> p1;
shared_ptr<int> p2 = make_shared<int>(42);
shared_ptr<string> p3 = make_shared<string>(10,'9');
shared_ptr<int> p4 = make_shared<int>();//值初始化
auto p5 = make_shared<vector<string>>();

拷贝和赋值

  • 拷贝:递增计数器
auto p = make_shared<int>(42);
auto q(p);
  • 赋值:递减等号左边的计数器,递增等号右边的计数器
auto r = make_shared<int>(42);
r = q;//递减r指向对象的引用计数,递增q指向对象的引用计数
  • 当指向一个对象的最后一个shared_ptr被销毁时,shared_ptr会自动销毁指向的对象,并释放相关联的内存。

其他操作

  • *p

  • p.get():返回内置指针;

  • swap(p,q):交换指针;

  • p.use_count():返回与p共享对象的智能指针数量;

  • p.unique():如果use_count返回1,则返回true,否则返回false;

  • p.reset():若p是唯一指向其对象的shared_ptr,则释放此对象;如果不是,就把p置空;

  • p.reset(q):若p是唯一指向其对象的shared_ptr,则释放此对象;令p指向q;

    shared_ptr<int> p(new int(1024));//正确,使用普通指针初始化智能指针
    p = new int(2048);               //错误,普通指针不能转化为智能指针
    p.reset(new int(2048));//正确,p指向一个新对象,并释放原有对象(因为p是唯一指向原对象的智能指针)
    
    shared_ptr<int> p1(new int(1024));
    shared_ptr<int> p2(p1);
    p1.reset(new int(2048));//此时p1指向2048,而p2指向1024
    
  • reset()经常与unique()一起使用,来控制多个shared_ptr共享的对象:

    if(!p.unique())
    	p.reset(new string(*p)); //如果p不是独有该对象,则先制作一份拷贝,使得p指向拷贝的对象
    *p+=newVal; //在拷贝对象上进行操作,不会影响其他共享的智能指针
    

unique_ptr类

  • 某个时刻只能有一个unique_ptr指向一个给定对象;

不支持拷贝和赋值!

unique_ptr<int> p1=new int(1024);  //错误!!不能转换
unique_ptr<int> p1(new int(1024)); //正确!!
unique_ptr<int> p2(p1);            //错误!!不能拷贝
unique_ptr<int> p2=p1;             //错误!!不能赋值
  • 函数的实参传递和返回值也是拷贝,但是由于返回值拷贝之后,原unique_ptr会被销毁,因此返回一个unique_ptr是正确的,但是实参传递不支持拷贝。

操作:

  • up.get():返回内置指针;
  • up.release()up置空,返回普通指针但是不释放up指向的对象
  • up.reset()释放up指向的对象;up置空
  • up.reset(q)释放up指向的对象(因为up是唯一的指针);令up指向q(新对象)
  • 通过调用release()reset()实现“拷贝和赋值”:
unique_ptr<int> p1(new int(1024));//                            p1->1024
unique_ptr<int> p2(p1.release()); //将对象的所有权从p1转到p2;      p2->1024,p1->NULL
unique_ptr<int> p3(new int(2048));                           // p3->2048
p2.reset(p3.release());//释放p2指向的对象,然后p2指向p3             p2->2048, p3->NULL
p2.reset(new int(4096));                                     // p2->4096

weak_ptr类

  • 指向一个由shared_ptr管理的对象,一旦最后一个指向对象的shared_ptr被销毁,对象就会被释放,即使有weak_ptr指向该对象。

初始化

  • 只能用shared_ptr来初始化weak_ptr:
auto p=make_shared<int>(42);
weak_ptr<int> wp(p); //p指向的对象的计数不会增加

拷贝和赋值

shared_ptr<int> p=make_shared<int>(42);
weak_ptr<int> wp=p; //可以将shared_ptr转化为weak_ptr;

操作:

  • wp.reset():wp置空;

  • wp.use_count():返回与wp共享的shared_ptr的数量;

  • wp.expired():如果use_count()为0,返回true,否则返回false;

  • wp.lock():如果expired()为true,返回空shared_ptr指针,否则返回一个指向wp对象的shared_ptr;

  • 由于weak_ptr指向的对象可能不存在,因此不能使用weak_ptr直接访问对象:

    if(shared_ptr<int> np=wp.lock()){*np++}
    

weak_ptr的作用

  • weak_ptr 设计的目的是为配合 shared_ptr 而引入的一种智能指针来协助 shared_ptr 工作, 它只可以从一个 shared_ptr 或另一个 weak_ptr 对象构造, 它的构造和析构不会引起引用记数的增加或减少。weak_ptr是用来解决shared_ptr相互引用时的死锁问题,如果说两个shared_ptr相互引用,那么这两个指针的引用计数永远不可能下降为0,资源永远不会释放。

    class B;
    class A{
    public:
    	shared_ptr<B> pb_;
    	~A(){cout<<"A delete\n";}
    };
    
    class B{
    public:
    	shared_ptr<A> pa_;
    	~B(){cout<<"B delete\n";}
    };
    
    void fun(){
    	shared_ptr<B> pb(new B());
    	shared_ptr<A> pa(new A());
    	pb->pa_ = pa;
    	pa->pb_ = pb;
    	cout<<pb.use_count()<<endl;
    	cout<<pa.use_count()<<endl;
    }
    
    int main(){
    	fun();
    	return 0;
    }
    

在这里插入图片描述

  • 这种情况下,指向对象A和对象 B指针计数都是2,当退出函数fun()之后,引用计数分别减1,这样对象A和对象B的引用计数都变成1,无法释放内存;
  • 如果将其中一个shared_ptr改为weak_ptr,由于weak_ptr不改变对象的引用计数,所以fun()执行时的引用计数分别为2和1,退出fun()之后,分别递减成1和0,导致B对象被释放,释放B的同时也会让A的计数减1,这样AB可以全部被释放;
    在这里插入图片描述
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章