C++智能指针

前言

C++中没有像Java一样提供自动回收机制,程序员想要操作堆内存只能使用指针通过new或者malloc开辟,delete或者free释放。在日常工作中经常出现程序员没有准确的释放内存,有可能导致内存泄露,或者调用了一块已经被释放的内存而出现种种问题。智能指针的出现有效的解决了这类问题。智能指针是模板类而不是指针。智能指针实现基本原理原理是-资源分配即初始化RAII(Resource Acquisition Is Initialization)。具体做法就是定义一个类来封装资源的分配和释放,在构造函数中完成资源的分配和初始化,在析构函数中完成资源的释放,可以保证资源正确的初始化和释放。下面就来介绍下几种常见的智能指针。

auto_ptr

auto_ptr是C++98加入标准库的智能指针,auto_ptr可以使用->和*(重载了这两个操作符)直接操作auto_ptr定义的智能指针;auto_ptr也有很多弊端,auto_ptr不能共享所有权;不能指向数组(因为析构函数调用的是delete而非delete[]);不能作为容器对象,因为STL容器中元素需要经常支持拷贝赋值等操作,而auto_ptr不能共享所有权所以必然不能在STL中使用,C++11以后版本已经弃用了auto_ptr。下面用例子说明下auto_ptr:

    int *a=new int(10);
    std::auto_ptr<int> p1(a);

上面代码如果想对指针a进行操作,会崩溃,就是因为auto_ptr不能共享所有权的特性导致的,在auto_ptr的拷贝构造函数中会将原来的指针置NULL,所以auto_ptr的最大缺点就是有内存崩溃的潜在风险,当你无意中使用到了指针a就会崩溃。

    int *a=new int(10);
    std::auto_ptr<int> p1(a);
    std::auto_ptr<int> p2(a);
    p2=p1;

上面代码如果使用p1指针依然会崩溃,因为auto_ptr的从写的赋值运算符函数也将入参置NULL,为了维护auto_ptr不能共享所有权的特性。所以个人推荐在程序中尽量不要使用auto_ptr智能指针,避免产生崩溃。
头文件:
#include
主要成员函数:
get():返回智能指针指向的内存地址

  int *a=new int(10);
  std::auto_ptr<int> p1(a);
  qDebug()<<*(p1.get());//qt打印,打印出来的结果是10

reset():重新设置智能指针指向的对象

    int *a=new int(10);
    std::auto_ptr<int> p1(a);
    p1.reset(new int(20));
    qDebug()<<*(p1.get());//打印出来的结果是20

release():返回智能指针的内存地址并释放对象的所有权

    int *a=new int(10);
    std::auto_ptr<int> p1(a);
    std::auto_ptr<int> p2(p1.release());
    qDebug()<<*p2;

unique_ptr

unique_ptr是C++11提过的用于替换auto_ptr的智能指针,unique_ptr可以使用->和*直接操作智能指针,并且具有auto_ptr不能共享所有权的特性。但是unique_ptr提供的方案是不允许智能指针作为右值赋值给别人,如果你一定要将一个unique_ptr赋值给其他变量编译器会报错(因为unique_ptr删除了赋值运算符函数),而不会像auto_ptr一样编译器默许但是使用的时候直接崩溃。在C++14版本后可以使用make_unique初始化unique_ptr。

    int *a=new int(10);
    std::unique_ptr<int> p1(a);//通过get方法获取到的地址和a相同
    //std::unique_ptr<int> p1=std::make_unique<int>(*a);//通过get方法获取到的地址和a不同
    std::unique_ptr<int> p2;
    p2=p1;

同样的例子使用unique_ptr声明的智能指针在p2=p1;进行赋值的时候编译器会报错。不允许这种赋值通过编译。

    int *a=new int(10);
    std::unique_ptr<int> p1(a);
    std::unique_ptr<int> p2(a);

上面代码中如果在后面使用了指针p1或者p2程序依然会崩溃,因为两个unique_ptr指向同一块内存,在释放的时候会出现重复释放的问题。
头文件:
#include
主要成员函数:
get():返回智能指针指向的内存地址

  int *a=new int(10);
  std::unique_ptr<int> p1(a);
  qDebug()<<*(p1.get());//qt打印,打印出来的结果是10

reset():重新设置智能指针指向的对象

    int *a=new int(10);
    std::unique_ptr<int> p1(a);
    p1.reset(new int(20));
    qDebug()<<*(p1.get());//打印出来的结果是20

release():返回智能指针的内存地址并释放对象的所有权

    int *a=new int(10);
    std::unique_ptr<int> p1(a);
    std::unique_ptr<int> p2(p1.release());
    qDebug()<<*p2;

shared_ptr

shared_ptr不同于unique_ptr和auto_ptr,shared_ptr允许多个指针指向同一个对象,shared_ptr通过计数来实现对内存的管理,在构造函数中每次新指针指向这块内存,计数器就会加1析构函数中每次有一个指针不需要指向这块内存计数减1,。如果计数变为0,那么就表示没有指针与这块内存关联就可以释放这块内存了。
头文件:
#include
主要成员函数:
swap:交换内存

    int *n1=new int(10);
    int *n2=new int(20);
    std::shared_ptr<int> p1(n1);
    std::shared_ptr<int> p2(n2);
    qDebug()<<*p1<<*p2;//打印出来是10 20
    p1.swap(p2);
    qDebug()<<*p1<<*p2;//打印出来是20 10

use_count():获取引用计数值,共享指针为空时返回0

    int *n1=new int(10);
    std::shared_ptr<int> p1(n1);
    qDebug()<<p1.use_count();//输出是1
    std::shared_ptr<int> p2(p1);
    qDebug()<<p1.use_count();//输出是2

reset():引用计数器减1,并可以从新赋值

    int *n1=new int(10);
    int *n2=new int(20);
    std::shared_ptr<int> p1(n1);
    qDebug()<<p1.use_count();//输出是1
    std::shared_ptr<int> p2(p1);
    qDebug()<<p1.use_count();//输出是2
    p2.reset(n2);
    qDebug()<<p1.use_count();//输出是1,因为p2重新指向了n2

get():获取原始指针

    int *n1=new int(10);
    std::shared_ptr<int> p1(n1);
    qDebug()<<n1;//打印出地址
    qDebug()<<p1.get();//和上面打印出的地址相同

unique():检测是否唯一

    int *n1=new int(10);
    std::shared_ptr<int> p1(n1);
    qDebug()<<p1.unique();//输出为true
    std::shared_ptr<int> p2(p1);
    qDebug()<<p1.unique();//输出为false

还需要介绍1个使用shared_ptr常用到的函数但此函数并不是它的成员函数:
make_shared:构造共享指针

    std::shared_ptr<int> p1=std::make_shared<int>(10);
    qDebug()<<*p1;//输出为10

使用make_shared的好处主要是减少内存分配的次数可以一次分配内存,如果使用指针先new然后在用shared_ptr构造智能指针需要分配两次内存,new时候分配一次构造智能指针时候又分配了一次,如果构造函数不是public的话无法使用make_shared。

weak_ptr

虽然weak_ptr被定义为智能指针,但是该类型的指针通常不单独使用,只能和shared_ptr类型指针搭配使用。所以可以将weak_ptr视为shared_ptr的辅助工具。可以通过weak_ptr获取shared_ptr指针的一些状态。当weak_ptr和shared_ptr指向相同时,weak_ptr创建和释放并不会影响shared_ptr计数器的变化。通常wead_ptr的指针会指向share_ptr指针拥有的内存空间。
头文件:
#include
主要成员函数:
swap():交换内存

    std::weak_ptr<int> pw(new int(10));
    std::weak_ptr<int> pw2(new int(20));
    pw.swap(pw2);

reset():将当前weak_ptr指针置空

    std::weak_ptr<int> pw(new int(10));
    pw.reset();

use_count():查看weak_ptr指向的shared_ptr指针的数量

    std::shared_ptr<int> ps=std::make_shared<int>(10);
   std::weak_ptr<int> pw(ps);
   qDebug()<<pw.use_count();//输出为1
   std::shared_ptr<int> ps2(ps);
   qDebug()<<pw.use_count();//输出为2

expired():判断当前weak_ptr指针是否过期(指针为空,或者指针指向的内存被释放)

    std::shared_ptr<int> ps=std::make_shared<int>(10);
   std::weak_ptr<int> pw(ps);
   qDebug()<<pw.expired();//输出为false,表示没过期
   ps.~__shared_ptr();
   qDebug()<<pw.expired();//输出为true,表示过期

lock():返回指向的shared_ptr指针,如果指针过期返回的shared_ptr指针为空

    std::shared_ptr<int> ps=std::make_shared<int>(10);
   std::weak_ptr<int> pw(ps);
   if(NULL == pw.lock()){
       qDebug()<<"null 1";
   }
   ps.~__shared_ptr();
   if(NULL == pw.lock()){
       qDebug()<<"null 2";//输出为null 2,证明此时weak_ptr指向的shared_ptr指针已过期
   }

以上内容均是作者查阅资料加自己理解,如有疑问,望读者不吝赐教,谢谢!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值