C++11智能指针

C++11智能指针

本文将讨论C++ 11 中的智能指针,如何使用以及注意事项。

share_ptr

shared_ptr是c ++ 11提供的一种Smart Pointer类,它能够在不再被引用的时候自动释放内存,从而帮助我们消除内存泄漏和指针悬空的问题。shared_ptr 对象内部指向两个内存,分别是目标对象的内存以及一个引用计数器。当shared_ptr关联到一个指针时,它会让引用计数器加1,当shared_ptr对象离开作用域时,将调用析构函数,在析构函数内部,它会将引用计数减1,如果引用计数的新值为0,则它将删除关联的原始指针。

  • 创建shared_ptr对象并初始化

    shared_ptr<int> s1(new(int));	
    
  • 创建shared_ptr 对象当暂时未设置目标指针

    shared_ptr<int> s3 = make_shared<int>();	//此时值为nullptr,引用计数器为1
    
  • 查看shared_ptr对象的引用计数器

    int count = s1.use_count();
    
  • 重置shared_ptr

    s2.reset();		//引用计数减1,如果引用计数变为0,则删除指针,否则值变为nullptr。
    s2.reset(new int(1));	//相当于reset()之后再重新赋值
    s2 = nullptr;	//也可以这样
    
  • 普通使用

    shared_ptr是伪指针,我们可以将*和->与shared_ptr对象一起使用,也可以像其他shared_ptr对象一样对其进行比较;

    shared_ptr<int> s1(new int(111));
    shared_ptr<int> s2 = s1;
    if(s1==s2){
        s2 = nullptr;
    }
    *s2 = 99;
    cout << *s1 << endl;
    cout << s1.use_count() << "  " << s2.use_count() << endl;
    if(!s2){
        cout<<"s2 is null";
    }		
    
  • 进阶:自定义释放内存方式

    shared_ptr 默认使用delete 方式来释放指向的对象,但是若该对象是数组,则析构函数不能正常完成工作。此时需要为shared_ptr提供一个自定义的析构函数。注意模板参数里使用int 而非int*

    //方法1:Lambda表达式
    shared_ptr<int>(new int[1000], [](int* i){delete[] i; });
    //方法2:函数指针
    void deleter(int *s){
    	delete[] s;
    }
    shared_ptr<int>(new int[1000], &deleter);
    
  • 细节&注意事项

    shared_ptr的主要优点是,当不再使用它时,它将自动释放关联的内存。但是,如果我们不小心使用shared_ptr,那么这种优势就会变成劣势。

    1. 不可以使用以下方式声明

      shared_ptr<int> s1 = new int();				//编译错误,
      
    2. shared_ptr没有提仅提供了->, * ,以及比较运算符,没有+、++、[]等运算符。

    3. 可以通过get函数来访问shared_ptr内部的原始指针,但是不推荐使用这样的方式,因为可能导致访问已释放内存的问题。

      shared_ptr<int> s1(new int[10], deleter);
      int *s2 = s1.get();
      
    4. 不要使用裸指针为智能指针赋值,这会导致删除已释放的内存,引发程序崩溃。

      int *rawPtr = new int();
      shared_ptr<int> ptr1(rawPtr);
      shared_ptr<int> ptr2(rawPtr);
      
    5. 不要关联到栈内存,否则该内存会被释放两次,引发程序崩溃。

      void demo(){
      	int i = 999;
      	shared_ptr<int>ptr(&i);
      	return;
      }
      
    6. shared_ptr 循环引用会导致内存泄漏,例如一条环状链表,在离开作用域后没有节点会被释放。解决这个问题可以使用weak_ptr。

      struct Node{
      	string name;
      public:
      	shared_ptr<Node> next;
      	Node(string n){
      		name = n;
      	}
      	~Node(){
      		cout << name << " have been delete...\n";
      	}
      };
      
      void demo(){
      	shared_ptr<Node> n1(new Node("n1"));
      	shared_ptr<Node>n2(new Node("n2"));
      	n2->next = n1;	//n1和n2为一条链
      	shared_ptr<Node> n3(new Node("n3"));
      	shared_ptr<Node>n4(new Node("n4"));
      	n3->next = n4;
      	n4->next = n3;	//n3和n4形成了环
      }
      
      int main(){
      	demo();	//只用n1、n2被释放
      	return 0;
      }
      

weak_ptr

weak_ptr 允许共享但不拥有对象,它的对象是由shared_ptr创建的。使用weak_ptr对象,我们不能直接使用运算符*和->来访问关联的内存,必须通过调用weak_ptr对象的lock()函数来创建一个shared_ptr,然后才能使用它。

  • 创建示例

    shared_ptr<int> share = make_shared<int>(100);
    weak_ptr<int> weak(share);
    
  • 使用示例

    weak_ptr 创建时使shared_ptr 的弱引用增加1,弱引用计数不会妨碍shared_ptr 资源的释放。weak_ptr 需要通过expired()方 法判断资源是否仍然有效以及lock()方法使shared_ptr强引用+1,然后才能使用指向的资源,若shared_ptr已经释放,lock()将返回一个空的shared_ptr。这样的特性可以解决循环引用时shared_ptr不能正常释放资源的问题。

    void demo2(){
    	shared_ptr<int> shareP = make_shared<int>(100);
    	weak_ptr<int> weakP(shareP);
    	auto tmp = weakP.lock();
    	cout << "use_count is " << shareP.use_count() << endl;
    	if (tmp){
    		cout << *tmp << endl;
    		tmp.reset();
    	}
    	cout << "use_count is " << shareP.use_count() << endl;
    	if (!weakP.expired()){	
    		cout << "weap_ptr not expired..." << endl;
    	}
    	shareP.reset();
    	if (weakP.expired()){
    		cout << "weap_ptr expired..." << endl;
    	}
    }
    
  • 使用weak_ptr解决之前内存泄漏的问题

    将Node结构体中的next类型又shared_ptr改成weak_ptr即可。

unique_ptr

unique_ptr 始终是关联的原始指针的唯一所有者,我们不能复制unique_ptr,只能移动。当unique_ptr离开作用域或资源被其它资源重写了,则会自动释放之前的资源。所以它保证了他所关联的资源总是能被释放。

  • 创建对象

    unique_ptr 接受原始指针作为参数。并且已经删除了赋值运算符。

    unique_ptr<int> p(new int(100));
    unique_ptr<int> p2 = new int(100);	//编译错误
    
  • 关于重置

    unique_ptr 调用reset()函数将对其进行重置,即它将删除关联的原始指针并使对象为空。

    unique_ptr<int> p(new int(100));
    p.reset();
    if (p == nullptr){
    	cout << "Null" << endl;
    }
    
  • 关于复制

    unique_ptr 是不可复制的,只能移动。因此,无论是通过拷贝构造函数还是赋值运算符,我们都无法创建unique_ptr对象的拷贝。

    unique_ptr<int> p1(new int(100));
    unique_ptr<int> p2 = p1;	//编译错误
    unique_ptr<int> p3(p1);		//编译错误
    
  • 转移对象的所有权

    unique_ptr不能复制,只能移动。这意味着unique_ptr对象可以将关联的原始指针的所有者转移到另一个unique_ptr对象。

    unique_ptr<int> p1(new int(100));
    unique_ptr<int> p2 = move(p1);
    if (p1 == nullptr){
    	cout << "P1 is null...\n";
    }
    
  • 释放原始指针

    在unique_ptr对象上调用release()将释放该对象的关联原始指针的所有权。
    它返回原始指针。注意release仅是释放所有权,不会释放原始指针的内存。

    unique_ptr<int> p1(new int(100));
    int *val = p1.release();
    cout << *val << endl;	//100
    if (p1 == nullptr){
    	cout << "p1 is null" << endl;
    }
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值